I have this constant:
export const clientData = fetch(`${process.env.SERVER_HOST}clientData.json`)
.then(response => response.json());
Which works properly, and Now I'm working on the test of this, with Jasmine and fetch-mock
This is my test:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', () => {
console.log('here', clientData);
var a = clientData;
a.then(b=> console.log(b))
});
});
The console.log of clientData returns a Promise (Which is fine), but the then is never triggered.
Not seeing why, what is wrong with my code?
This happens because the test execution is synchronous in nature and it doesn't wait for the assertion to happen, so you have to pass a done callback and call it from your test inside the then callback
Like this:
import { clientData } from '../../../src/js/services/client-data.fetch';
import fetchMock from 'fetch-mock';
describe('test', () => {
const exampleResponse = {
clientData: 'test'
};
beforeAll(() => {
fetchMock.mock('*', exampleResponse);
});
it('ooo', (done) => {
console.log('here', clientData);
var a = clientData;
a.then(b=> {
console.log(b);
done();
})
});
});
Related
I'm trying to test geUrl() function in the file.
The first test case is passing but the second one will fail.
Because isFromApp is called only once on load of the file. I could not find a way to change the mock for it.
Any one know how can I mock different value for isFromApp for the second test case?
util.js
import { fromApp } from 'utils/domUtil';
export const isFromApp = fromApp('query', 'app');
export function getUrl() {
if (isFromApp) return 'is from app';
return 'is from web';
}
util.test.js
import { getUrl } from './util';
jest.mock('utils/domUtil', () => {
const actual = jest.requireActual('utils/domUtil');
return {
...actual,
fromApp: jest.fn().mockReturnValueOnce(true).mockReturnValueOnce(false),
};
});
describe('util', () => {
describe('getUrl', () => {
it('should return from app', () => {
expect(getUrl()).toEqual('is from app');
});
it('should return from web', () => {
expect(getUrl()).toEqual('is from web');
});
});
});
im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request.
like this:
const makeThirdPartyServiceRequest = middy(async ({ params }) => {
logger.info(`SENDING Request to ${endpoint} API`)
const url = `https://someurltoathirdpartyservice`
const options = {
method: 'POST',
body: params
}
return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))
However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:
jest.mock('../src/thirdPartyService', () => ({
__esModule: true,
default: {
...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
makeThirdPartyServiceRequest: jest.fn()
}
}))
export {}
import thirdPartyService from '../src/thirdPartyService'
And then in the test i say:
describe('makeThirdPartyServiceRequest()', () => {
it('should makeThirdPartyServiceRequest', async () => {
// Given
// })
const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
// When
const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
// Then
expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
})
})
However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?
You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.
import thirdPartyService from '../src/thirdPartyService'
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
}
}
})
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
hoangdv answer is also valid, but i will answer as well how i continued.
if you completely want to mock middy you mock like following:
jest.mock('#middy/core', () => {
return (handler) => {
return {
use: jest.fn().mockImplementation(() => {
// ...use(ssm()) will return handler function
return {
before: jest.fn().mockReturnValue(handler)
}
})
}
}
})
However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:
jest.doMock('#middy/util', () => ({
...(jest.requireActual('#middy/util') as {}),
getInternal: jest.fn()
}))
import { getInternal } from '#middy/util'
and then in the test
describe('thirdPartyService()', () => {
beforeEach(() => {
jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
})
describe('makeThirdPartyServiceRequest', () => {
it('should make a request with correct parameters', async () => {
// Given
const url = `https://someurltoathirdpartyservice`
const params = 'any params'
const apiResponse = 'any response'
mocked(getInternal).mockResolvedValue({
twilioSecrets: { accountSid: 'someSID', serviceId:
'someServiceID', token: 'someToken' }
})
mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
// When
const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
// Then
expect(actual).toBe(apiResponse)
expect(helpers.makeRequest).toHaveBeenCalledWith(
url,
{
method: 'POST',
body: params
}
)
})
})
})
this will mock the async part of middy.
zoomOut(callback) {
// Zooms out the current screen
this.view.current.zoomOut(300).done(() => {
(hasCallback(callback)) && callback();
});
}
I'm trying to test the function above but I keep getting the following error:
TypeError: this.view.current.zoomOut(...).done is not a function
How can I mock this method chain in Jest?
Thanks to BudgieInWA, I was able to solve this problem by returning done.
For those who are testing a React component with Enzyme, here's how you can do it:
it('should call callback', () => {
const wrapper = shallow(<Zoom {...minProps}/>);
const instance = wrapper.instance();
const callback = jest.fn();
instance.view = {
current: {
zoomOut: jest.fn(() => {
return {
done: jest.fn((callback) => {
callback();
})
};
})
}
};
expect(callback).toHaveBeenCalledTimes(0);
instance.zoomOut(callback);
expect(callback).toHaveBeenCalledTimes(1);
});
You could try this:
const mockZoomOut = jest.fn(() => ({ done(cb) { cb(); } }));
const mockThis = {
view: {
current: {
zoomOut: mockZoomOut,
},
},
};
test('it does', () => {
const cb = jest.fn();
zoomOut.apply(mockThis, [cb]);
expect(mockZoomOut).toHaveBeenCalledTimes(1);
expect(cb).toHaveBeenCalledTimes(1);
});
See Jest Mock Functions and fn.apply.
If you are testing the behaviour of the class as a whole, then you could set up the instance that you are testing to have this.view.current.zoomOut be mockZoomOut somehow.
Below I have a test for my login actions. I'm mocking a Firebase function and want to test if the signIn/signOut functions are called.
The tests pass. However, I do not see my second console log. Which is this line console.log('store ==>', store);.
it('signIn should call firebase', () => {
const user = {
email: 'first.last#yum.com',
password: 'abd123'
};
console.log('111');
return store.dispatch(signIn(user.email, user.password)).then(() => {
console.log('222'); // Does not reach
expect(mockFirebaseService).toHaveBeenCalled();
});
console.log('333');
});
● login actions › signIn should call Firebase
TypeError: auth.signInWithEmailAndPassword is not a function
Action being tested
// Sign in action
export const signIn = (email, password, redirectUrl = ROUTEPATH_DEFAULT_PAGE) => (dispatch) => {
dispatch({ type: USER_LOGIN_PENDING });
return firebase
.then(auth => auth.signInWithEmailAndPassword(email, password))
.catch((e) => {
console.error('actions/Login/signIn', e);
// Register a new user
if (e.code === LOGIN_USER_NOT_FOUND) {
dispatch(push(ROUTEPATH_FORBIDDEN));
dispatch(toggleNotification(true, e.message, 'error'));
} else {
dispatch(displayError(true, e.message));
setTimeout(() => {
dispatch(displayError(false, ''));
}, 5000);
throw e;
}
})
.then(res => res.getIdToken())
.then((idToken) => {
if (!idToken) {
dispatch(displayError(true, 'Sorry, there was an issue with getting your token.'));
}
dispatch(onCheckAuth(email));
dispatch(push(redirectUrl));
});
};
Full Test
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
// Login Actions
import {
// onCheckAuth,
signIn,
signOut
} from 'actions';
import {
// USER_ON_LOGGED_IN,
USER_ON_LOGGED_OUT
} from 'actionTypes';
// String Constants
// import { LOGIN_USER_NOT_FOUND } from 'copy';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
// Mock all the exports in the module.
function mockFirebaseService() {
return new Promise(resolve => resolve(true));
}
// Since "services/firebase" is a dependency on this file that we are testing,
// we need to mock the child dependency.
jest.mock('services/firebase', () => new Promise(resolve => resolve(true)));
describe('login actions', () => {
let store;
beforeEach(() => {
store = mockStore({});
});
it('signIn should call firebase', () => {
const user = {
email: 'first.last#yum.com',
password: 'abd123'
};
console.log('111');
return store.dispatch(signIn(user.email, user.password)).then(() => {
console.log('222'); // does not reach
expect(mockFirebaseService).toHaveBeenCalled();
});
console.log('333');
});
it('signOut should call firebase', () => {
console.log('signOut should call firebasew');
store.dispatch(signOut()).then(() => {
expect(mockFirebaseService).toHaveBeenCalled();
console.log('store ==>', store);
expect(store.getActions()).toEqual({
type: USER_ON_LOGGED_OUT
});
});
console.log('END');
});
});
You have two issues here,
The tests pass however I do not see my 2nd console log. Which is this
line console.log('store ==>', store);.
That is because the test is not waiting for the promise to fulfill, so you should return it:
it('signOut should call firebase', () => {
console.log('signOut should call firebasew');
return store.dispatch(signOut()).then(() => { // NOTE we return the promise
expect(mockFirebaseService).toHaveBeenCalled();
console.log('store ==>', store);
expect(store.getActions()).toEqual({
type: USER_ON_LOGGED_OUT
});
console.log('END');
});
});
You can find examples in the Redux official documentation.
Secondly, your signIn test is failing because you have mocked the wrong firebase:
jest.mock('services/firebase', () => new Promise(resolve => resolve(true)));
That should probably look more like:
jest.mock('services/firebase', () => new Promise(resolve => resolve({
signInWithEmailAndPassword: () => { return { getIdToken: () => '123'; } }
})));
Login actions › signIn should call firebase
TypeError: auth.signInWithEmailAndPassword is not a function
This tells that your store.dispatch(signIn(user.email, user.password)) fails, thus your second console.log won't go into your then chain, use catch or second callback argument of then instead.
I'm trying to test a try catch block using Jest. E.g.
// filename: foo.js
const foo = () => {
let js = 'somejs'
try {
eval(js)
} catch (e) {
document.location = 'redirect/to/page'
return false
}
return true
}
export default foo
Do you mock eval (not my choice to use eval. It's my bosses code) and then get it to throw? If so, how would you do that? E.g.
eval = jest.fn() // doesn't work
So the test code might be:
// filename: foo.spec.js
import foo from './foo'
eval = jest.fn()
describe('foo', () => {
describe('when eval succeeds', () => {
it('performs an eval', {
const result = foo()
expect(result).toEqual(true)
})
describe('when eval fails', () => {
beforeEach(() => {
eval.mockImplementation(() => {throw 'error'})
})
it('catches the error and redirects', () => {
const result = foo()
expect(result).toEqual(false)
})
})
})
What would the mocking and test code look like?