How to mock optimizelySDK.createInstance().onReady() using Jest? - javascript

Here is my mock file __mocks__/#optimizely/optimizely-sdk.js
const optimizelySDK = jest.requireActual('#optimizely/optimizely-sdk')
optimizelySDK.createInstance().onReady = () => ({ success: false }))
module.exports = optimizelySDK
Here is my test file Optimizely.test.js
import optimizelySDK from '#optimizely/optimizely-sdk'
test('onReady', () => {
const response = optimizelySDK.createInstance().onReady()
expect(response).toBe({ success: false })
})
I think I might be going about this all wrong. This worked perfectly when I try this with lodash. I believe this is because optimizelySDK is a class. I think I should be mocking that instead. How do I successfully mock and test optimizelySDK?

For anyone who came across this on Google, I had the same problem and got it working with jest:
jest.mock('#optimizely/optimizely-sdk', () => ({
...jest.requireActual('#optimizely/optimizely-sdk'),
createInstance: () => ({
getEnabledFeatures: jest.fn().mockReturnValueOnce(['featureA', 'featureB']),
onReady: jest.fn().mockResolvedValueOnce({ status: 200 })
})
}))
describe('my-test', () => {
it('should pass', async () => {
const result = await getFeatures()
console.log(result) // ['featureA', 'featureB']
// assert on result here
});
});
where my code looked something like:
const getFeatures = async (event) => {
try {
const optimizelyInstance = optimizelySDK.createInstance({
sdkKey: process.env.OPTIMIZLEY_SDK_KEY,
});
const optimizelyParameters = {}
return optimizelyInstance.onReady().then(() => {
const result = optimizelyInstance.getEnabledFeatures('id', optimizelyParameters);
return result;
});
} catch (err) {
console.error('Could not get features', err);
}
};

Related

get new Instance for function each test

I have some tests in one file,
I check my reducer with some case
My code looks like this
my code
import axiosInstance from '~/utils/network';
const fetcher = axiosInstance();
const fetchMiddleware = () => {
switch (type) {
case 'LOGOUT':{
try {
await fetcher.get(API.GET.LOGOUT_OPERATION);
dispatch({ type: 'LOGOUT_SUCCESS' });
} catch (err) {
dispatch({ type: 'LOGOUT_FAIL' });
}
});
}
}
}
my test
import axiosInstance from '../../src/utils/network';
import configureStore from 'redux-mock-store';
const middlewares = [fetchMiddleware, thunk];
const mockStore = configureStore(middlewares);
const store = mockStore(getInitialReducerState());
jest.mock('../../src/utils/network', () => {
const axiosInstance = jest.fn().mockImplementation(() => {
return {
get: jest.fn().mockImplementation(() => {
return {
headers: {},
};
}),
};
}) as any;
axiosInstance.configure = jest.fn();
return axiosInstance;
});
describe('test LOGOUT', () => {
beforeEach(() => {
store.clearActions();
});
it('should test be success', async () => {
await store.dispatch({
type: 'LOGOUT',
payload: { userName: 'testUserName' },
});
expect(store.getActions()).toContainEqual({
type: 'LOGOUT_SUCCESS',
});
});
it('should test be fail', async () => {
(axiosInstance as jest.Mock).mockImplementation(() => {
return {
get: jest.fn().mockImplementation(() => {
throw new Error(' ');
}),
};
});
await store.dispatch({
type: 'LOGOUT',
payload: { userName: 'testUserName' },
});
expect(store.getActions()).toContainEqual({
type: 'LOGOUT_FAIL',
});
});
});
I want to test two scenarios: success & fail,
I mock the axiosInstance function.
But even I override the mock in the second test I get the first mock because my code loads axiosInstance only once.
what can I do?
You need to use jest.isolateModules
Let's say we have 2 files:
./lib.js - this is your ~/utils/network
./repro.js - this is your file with the code under test
./lib.js:
export default function lib() {
return () => 10;
}
./repro.js:
import lib from './lib';
const fnInstance = lib();
export const fn = () => {
return fnInstance();
};
And the ./repro.test.js:
function getRepro(libMock) {
let repro;
// Must use isolateModules because we need to require a new module everytime
jest.isolateModules(() => {
jest.mock('./lib', () => {
return {
default: libMock,
};
});
repro = require('./repro');
});
// If for some reason in the future the behavior will change and this assertion will fail
// We can do a workaround by returning a Promise and the `resolve` callback will be called with the Component in the `isolateModules` function
// Or we can also put the whole test function inside the `isolateModules` (less preferred)
expect(repro).toBeDefined();
return repro;
}
describe('', () => {
it('should return 1', () => {
const { fn } = getRepro(function lib() {
return () => 1
});
expect(fn()).toEqual(1);
});
it('should return 2', () => {
const { fn } = getRepro(function lib() {
return () => 2
});
expect(fn()).toEqual(2);
});
});
It's preferable to use existing library to mock Axios, it saves from boilerplate code and potential mistakes in mock implementation; moxios has been already suggested.
It's inconvenient to mock axiosInstance per test because it has been already called on the import of tested module, so this requires it to be re-imported per test; another answer explains how it's done with jest.isolateModules.
Since axiosInstance is evaluated only once and is supposed to return mocked object, it's convenient to mock it once per test and then change implementations:
jest.mock('~/utils/network', () => {
const axiosMock = { get: jest.fn(), ... };
return {
axiosInstance: () => axiosMock;
};
});
const axiosMock = axiosInstance();
...
(axiosMock.get axiosInstance as jest.Mock).mockImplementation(() => {
throw new Error(' ');
});
await store.dispatch(...);
This requires to use jest.restoreAllMocks in beforeEach or similar Jest configuration option to avoid test cross-contamination.
Notice that Axios doesn't throw errors but rather return rejected promises, this may affect test results, see the note regarding the benefits of libraries.

Hide console logs in JEST test

I'm new to JEST and testing in general and having trouble figuring out the following.
I have the following script that is part of a CLI tool.
I would like to stop the spinner outputs when testing.
I have tried spyOn/mock, but to no avail.
const ora = require('ora');
const spinner = new ora();
const chalk = require('chalk');
const fs = require('fs');
module.exports = path =>
new Promise((resolve, reject) => {
spinner.text = chalk.blue('Creating directory...');
spinner.start();
fs.mkdir(path, err => {
if (!err) {
spinner.succeed(chalk.bgGreen('Directory created\n'));
resolve(true);
} else {
spinner.fail(chalk.bgRed(`Directory already exists: ${path}`));
reject(err);
}
});
});
this is my test:
const createDir = require('./utils/createDir');
const fs = require('fs');
describe('createDir function', () => {
const folders = {
base: './.test',
fail: './.test/fail',
success: './.test/success'
};
beforeAll(() => {
fs.mkdirSync(folders.fail, { recursive: true });
});
afterAll(() => {
fs.rmdirSync(folders.base, { recursive: true });
});
it('creates the directory', async () => {
await expect(createDir(folders.success)).resolves.toBe(true);
});
it('fails if directory exists', async () => {
await expect(createDir(folders.fail)).rejects.toThrow();
});
});
You should be able to just add
jest.mock('ora')
in the beginning of your test. It will auto-mock the entire library replacing each of the methods with jest.fn() (without any implementation) so the calls from the implementation will have no effect on the output.
EDIT by Ben:
The functional mock turned out to be this:
jest.mock('ora', () => {
return jest.fn().mockImplementation(() => {
return {
start: () => {},
fail: () => {},
succeed: () => {}
};
});
});

How to test in JEST a request that downloads a file

I want to unit test the exported method in the code below. Trying to write unit tests for a function that is downloading a zip file from a localhost server.I will write my function bellow so you understand better:
export const downloadCdn = async (cdnUrl, out) => {
const download = (resolve, reject) => {
const req = request({
method: 'GET',
uri: cdnUrl
});
req.on('response', (data) => {
// do something
});
req.on('error', (data) => {
// do something
});
req.on('data', (chunk) => {
// do something
});
req.on('end', () => {
console.log('download done');
});
req.pipe(out);
out.on('close', () => {
resolve([null, 'done']);
});
};
const downloadSummary = new Promise(download);
return downloadSummary
.then(() => [null, 'Done'])
.catch(err => [err, null]);
};
Here are my test file, what I'm trying to achieve is to have unit test that validates the download of the zip file:
import request from 'request';
import * as Module from './downloadCdn';
jest.mock('request', () => {
const mockRequest = {
pipe: jest.fn(),
on: jest.fn(),
};
return function () {
return mockRequest;
};
});
describe('Downloading a file', () => {
it('Should find the module', () => {
expect(typeof Module.downloadCdn === 'function').toBeTruthy();
});
it('Should download the zip', async () => {
const [error, response] = await Module.downloadCdn(cdnUrl, out);
expect(response === 'Done').toBeTruthy();
expect(error === null).toBeTruthy();
});
});
The response from the Promise, I receive inside the test is null, no error catching. Here is the error received from jest:
expect(received).toBeTruthy()
Expected value to be truthy, instead received false
While mocking the request, you should resolve the promise. I think that promise is not resolving that's why it's not working. I hope that the below code will be fixed your problem.
jest.mock('request', () => {
const mockRequest = {
pipe: jest.fn(),
on: (parameter, callback) => {
callback();
},
};
return function () {
return mockRequest;
};
});

Jest/enzyme: How to test for .then and .catch of an nested asynchronous function

I'm trying to do some enzyme/jest unit testing for a asynchronous function in my reactJS component, which gets injected as prop.
My Problem is to test for a value in the then() part of the function and to test for catch() if an error occures.
This is how the function of my component (<CreateAccount />) looks like:
_onSubmit = (event) => {
event.preventDefault()
const { username, password } = this.state
this.props.createUserMutation({
variables: { username, password }
}).then(response => {
const token = response.data.createUser.token
if (token) {
Cookies.set('auth-token', token, { expires: 1 })
}
}).catch(error => {
console.warn(error)
})
}
The first test should check for .catch(error => {}) as data is undefined:
it('_onSubmit() should throw error if data is missing', () => {
const createUserMutation = () => {
return Promise.resolve({})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
expect(wrapper).toThrow() // <-- How to do it correctly?
})
And the second test should check if cookie is set correctly. Here I don't know how to do that? I think I have to mock Cookie
it('_onSubmit() should get token', () => {
const createUserMutation = () => {
return Promise.resolve({
data: {
createUser: { token: 'token' }
}
})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
})
// How to check for token value and Cookies?
})
What I usually have to do when I want to see if the spy worked on the catch or then, is to add another then() on the test. For example:
it('_onSubmit() should throw error if data is missing', () => {
const createUserMutation = jest.fn(() => Promise.reject(new Error()));
const spy = jest.spyOn(console,"warn");
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
});
return createUserMutation.catch(() => {
expect(wrapper).toMatchSnapshot();
})
.then(() => {
expect(spy).toHaveBeenCalledTimes(1);
});
})
I guess it is somehow related to how NodeJS handles the queues, promises, ticks, etc, internally.
That is the rejected/catch branch. If you want to test the IF path, just use a Promise.resolve and return a promise.then() instead of catch.
Why are you using console.warn for an error? Use console.error instead. You will need to mock it out to a spy as well to test it.
First test:
it('_onSubmit() should throw error if data is missing', (done) => {
const createUserMutation = () => new Promise();
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
createUserMutation.resolve().then(() => {
expect(console.warn).toHaveBeenCalled();
done();
});
})
If you are running this in a mock browser environment and not a real browser then you must mock out Cookies.set.
Second test:
it('_onSubmit() should get token', (done) => {
const createUserMutation = () => new Promise();
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
});
jest.spyOn(window.Cookies, 'set');
const response = {
data: {
createUser: { token: 'token' }
}
}
createUserMutation.resolve(response).then(() => {
expect(window.Cookies.set).toHaveBeenCalled();
done();
});
})
afterEach(() => {
// Reset the spies so that they don't leak to other tests
jest.restoreAllMocks();
});

How to set a test for multiple fetches with Promise.all using jest

I'm using jest for my tests. I'm using react and redux and I have this action:
function getData(id, notify) {
return (dispatch, ...) => {
dispatch(anotherFunction());
Promise.all(['resource1', 'resource2', 'resource3'])
.then(([response1,response2,response3]) => {
// ... handle responses
})
.catch(error => { dispatch(handleError(error)); }
};
}
I've been looking for into the jest documentation how to set a test for this action, but I was unable to find a way. I tried myself something like this:
it('test description', (done) => {
const expectedActions = [{type: {...}, payload: {...}},{type: {...}, payload: {...}},...];
fetchMock.get('resource1', ...);
fetchMock.get('resource2', ...);
fetchMock.get('resource3', ...);
// ... then the rest of the test calls
});
Unsuccessfully. So how should I proceed?
To use Promise.all you could do the following
test('Testing Stuff', async (done) => {
const expectedActions = [{ foo: {...}, bar: {...} }, { foo: {...}, bar: {...} }];
// we pass the index to this function
const asyncCall = async (index) => {
// check some stuff
expect(somestuff).toBe(someOtherStuff);
// await the actual stuff
const response = await doStuff( expectedActions[index] );
// check the result of our stuff
expect(response).toBe(awesome);
return response;
};
// we put all the asyncCalls we want into Promise.all
const responses = await Promise.all([
asyncCall(0),
asyncCall(1),
...,
asyncCall(n),
]);
// this is redundant in this case, but wth
expect(responses).toEqual(awesome);
done();
});
You can tell Jest to wait for the promise to resolve by returning the promise in the callback. See this section here for more info.
it('should fetch some food', () => {
const fetchItem1 = () => fetchData1().then(data => {
expect(data).toBe('peanut butter');
})
const fetchItem2 = () => fetchData2().then(data => {
expect(data).toBe('boiled egg');
})
const fetchItem3 = () => fetchData3().then(data => {
expect(data).toBe('fried salad');
})
return Promise.all([fetchItem1(), fetchItem2(), fetchItem3()])
.then(() => runOtherTests());
});

Categories