Import function into jest.mock to avoid boilerplate code - javascript

I assume this must be a pretty straightforward solution, but I am struggling to find a solution.
I have a function at the top of my tests:
jest.mock('../someFile', () => {
const setWidth = () => {
// lots of complex logic
};
return {
width: jest
.fn()
.mockImplementation(element => {
setWidth(element);
};
};
};
};
So, I understand that jest.mock is hoisted above the import statements in every test run, but say I would like to cut down on the boiler plate code I need in this file and as an example if setWidth was a really big function and I want to import it from another file, is there any way I could do this?
If I move setWidth to another file and try the following it fails due to the hoisting
import { setWidth } from ./setWidth
jest.mock('../someFile', () => {
return {
width: jest
.fn()
.mockImplementation(element => {
setWidth(element);
};
};
};
};
The error received is:
● Test suite failed to run
Invalid variable access: setWidth
Thanks in advance for any possible solutions!

jest.mock gets hoisted above the import, so that won't work. But what you can do is use requireActual
jest.mock('../someFile', () => {
const { setWidth } = jest.requireActual('./setWidth');
return {
width: jest
.fn()
.mockImplementation(element => {
setWidth(element);
};
};
};
};
Looks like you’re starting to go a bit crazy with "testing infrastructure" though - try to consider if there's a more "real" way (less testing infrastructure) you can test your code.
The most likely way to do this is to break the code your testing down into smaller functions/components.

Related

Jest custom testing API, how to correct code frame

I'm looking to simplify my project's testing API where I am aiming for something like this:
testThing((t) => {
t(33);
t(44);
t(42);
})
Now I don't know how to get Jest to show the correct code frames for failed expect's. This is my current stab at an implementation:
const testThing = (callback: any) => {
callback((n: any) => {
test(n.toString(), () => {
expect(n).toBe(42);
});
});
};
Which results in the testThing definition to be shown for every failed test case. Here's a replit if you want to see it in action: https://replit.com/#grgr/jest-frame-issue#thing.test.js

Jest: Mock a _HOC_ or _curried_ function

Given the following function:
./http.js
const http = {
refetch() {
return (component) => component;
}
}
I would like to mock the function in a test as follows:
./__tests__/someTest.js
import { refetch } from './http';
jest.mock('./http', () => {
return {
refetch: jest.fn();
}
}
refetch.mockImplementation((component) => {
// doing some stuff
})
But I'm receiving the error
TypeError: _http.refetch.mockImplementation is not a function
How can I mock the refetch function in the given example?
update:
When I modify the mock function slightly to:
jest.mock(
'../http',
() => ({ refetch: jest.fn() }),
);
I get a different error:
TypeError: (0 , _http.refetch)(...) is not a function
My guess it's something with the syntax where the curried function (or HOC function) is not mapped properly. But I don't know how to solve it.
Some of the real code I'm trying to test.
Note: The example is a bit sloppy. It works in the application. The example given is to give an idea of the workings.
./SettingsContainer
// ...some code
return (
<FormComponent
settingsFetch={settingsFetch}
settingsPutResponse={settingsPutResponse}
/>
);
}
const ConnectedSettingsContainer = refetch(
({
match: { params: { someId } },
}) => ({
settingsFetch: {
url: 'https://some-url.com/api/v1/f',
},
settingsPut: (data) => ({
settingsPutResponse: {
url: 'https://some-url.com/api/v1/p',
}
}),
}),
)(SettingsContainer);
export default ConnectedSettingsContainer;
Then in my component I am getting the settingsPutResponse via the props which react-refetch does.
I want to test if the user can re-submit a form after the server has responded once or twice with a 500 until a 204 is given back.
./FormComponent
// ...code
const FormComp = ({ settingsResponse }) => {
const [success, setSuccess] = useState(false);
useEffect(() => {
if (settingsResponse && settingsResponse.fulfilled) {
setSuccess(true);
}
}, [settingsResponse]);
if (success) {
// state of the form wil be reset
}
return (
<form>
<label htmlFor"username">
<input type="text" id="username" />
<button type="submit">Save</button>
</form>
)
};
The first question to ask yourself about mocking is "do I really need to mock this?" The most straightforward solution here is to test "component" directly instead of trying to fake out an http HOC wrapper around it.
I generally avoid trying to unit test things related to I/O. Those things are best handled with functional or integration tests. You can accomplish that by making sure that, given same props, component always renders the same output. Then, it becomes trivial to unit test component with no mocks required.
Then use functional and/or integration tests to ensure that the actual http I/O happens correctly
To more directly answer you question though, jest.fn is not a component, but React is expecting one. If you want the mock to work, you must give it a real component.
Your sample code here doesn't make sense because every part of your example is fake code. Which real code are you trying to test? I've seen gigantic test files that never actually exercize any real code - they were just testing an elaborate system of mocks. Be careful not to fall into that trap.

AVA: setup different timeout for each test case

I writing asynchronous tests using AVA, and need to setup custom timeout for each test cases. I've not found out any information about this possibility and my tests seems like this:
import test from 'ava';
test.cb('super test', t => {
setTimeout(() => {
t.is(1, 1);
t.end();
}, 10000);
setTimeout(() => {
t.fail("Timeout error!");
t.end();
}, 100);
});
Does anybody know another way to implement this in AVA?
There's an open issue to support this in AVA itself: https://github.com/avajs/ava/issues/1565
Until that lands you'll have to manage a timer yourself. Don't forget to clear it once your normal test completes.
I don't know if AVA has something like this built in. I suspect not, as it seems like quite an unusual use-case.
But you could create a utility function that implements some kind of a "timeout test":
import test from 'ava';
function timeout (ms, fn) {
return function (t) {
setTimeout(() => {
t.fail("Timeout error!")
t.end()
}, ms)
fn(t)
}
}
test.cb('super test', timeout(10000, t => {
t.is(1, 1);
}));

How to stub ES6 node_modules when using import?

I am a bit confused while writing tests. My stack is mocha, chai and sinon + babel to transpile. And recently I've started to use ES6 imports and exports. It's working great so far, but I have trouble with mocking some dependencies. Here is my case:
service.js
import {v4} from 'uuid';
function doSomethingWithUuid() {
return v4();
}
export function doSomething() {
const newUuid = doSomethingWithUuid();
return newUuid;
}
service.test.js
import {doSomething} from './service';
describe('service', () => {
it('should doSomething' () => {
// how to test the return of doSomething ?
// I need to stub v4 but I don't know how...
});
});
Things I've considered: sinon.stub, but I have not managed to make it work. Trying to import all uuid with import * as uuid from 'uuid'. But within my service.js, it's still
the original function which is called...
Plus as imports are supposed to be read-only, once it'll be native, this solution wouldn't work...
The only interesting thing I found on the net was this solution, to add a function in my service in order to let the outside world to override my dependencies.
(see https://railsware.com/blog/2017/01/10/mocking-es6-module-import-without-dependency-injection/).
import * as originalUuid from 'uuid';
let {v4} = originalUuid;
export function mock(mockUuid) {
({v4} = mockUuid || originalUuid);
}
This is ok to write this little boilerplate code, but it troubles me to add it in my code... I would prefer to write boilerplate in my test or some config. Plus, I don't want to
have an IoC container, I want to keep my functions as little as possible and stay as fonctional as I can...
Do you have any ideas? :)
You should be able to use a module like proxyquire for this. This is not tested code, but it would go something like the following:
const proxyquire = require('proxyquire');
const uuidStub = { };
const service = proxyquire('./service', { uuid: uuidStub });
uuidStub.v4 = () => 'a4ead786-95a2-11e7-843f-28cfe94b0175';
describe('service', () => {
it('should doSomething' () => {
// doSomething() should now return the hard-coded UUID
// for predictable testing.
});
});

jest support for mocking javascript 'classes'

I'm on a project that uses jest as the testing framework for a nodejs server. On one controller (called ControllerImTesting), it news up an instance of a helper class (we'll call ClassIWantToMock and uses it for the controller. I already have many tests written around ClassIWantToMock itself so naturally when I test ControllerImTesting, I just want to mock ClassIWantToMock out.
It's pretty simple, I've created another js file in __mocks__ that contains a dumb version of ClassIWantToMock. When I new it up in ControllerImTesting, I want it to use the dumb version in __mocks__.
I've tried lots of configurations at this point but my desperation move is to use setMock like:
jest.setMock('/path/to/real/class/I/want/to/mock', '/path/to/dumb/version') How could this fail?
But when running the test, I get TypeError: ClassIWantToMock is not a constructor.
I look at my dumb version of ClassIWantToMock and change the style from something like const ClassIWantToMock = (req) => { to class ClassIWantToMock {. I get the same error which kind of makes sense because using the es6 class style is just syntactic sugar.
Just to confirm, in my real controller, I write a line, console.log(ClassIWantToMock) above the line where it news up the instance. It indeed prints out the '/path/to/dumb/version'. It is trying to mock it but cannot.
Is this a limitation of Jest? Or am I simply not using it correctly? --> How should this be done?
UPDATE
./ClassIWantToMock.js
class ClassIWantToMock {
constructor(stuff) {
this.stuff = stuff
}
doStuff() {
console.log('some real stuff')
}
}
module.exports = ClassIWantToMock
./__mocks__/ClassIWantToMock.js
class ClassIWantToMock {
constructor(fakeStuff) {
this.fakeStuff = fakeStuff
}
doStuff() {
console.log('some fake stuff')
}
}
module.exports = ClassIWantToMock
./ControllerImTesting.js
const ClassIWantToMock = require('./ClassIWantToMock')
class ControllerImTesting {
static aMethod(req, res, next) {
const helper = ClassIWantToMock('real stuff')
helper.doStuff()
return next()
}
}
module.exports = ClassIWantToMock
./ControllerImTesting.spec.js
jest.setMock('./ClassIWantToMock', './__mocks__/ClassIWantToMock')
const ControllerImTesting = require('./ControllerImTesting')
describe('basic test', () => {
test('should work', () => {
return ControllerImTesting.aMethod({}, {}, () => {}).then(() => {
// expect console to display 'some fake stuff'
})
})
})

Categories