Jest Mock returns undefined instead of data - javascript

I'm trying to mock a function and not sure what i'm doing wrong here. I have this function "getGroups"
getGroups:
export const getGroups = async () => {
try {
const groupApiUrl = getDashboardPath(GROUPS_TAB_INDEX);
const data = await fetch(groupApiUrl, { cache: 'force-cache' });
const userData = await data.json();
return userData;
} catch (error) {
throw Error(error);
}
};
___mocks___/getGroups.js:
export default async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
getGroups.test.js:
jest.mock('./getGroups.js');
// eslint-disable-next-line import/first
import { getGroups } from './getGroups';
const fakeRespose = {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
describe('getGroups', () => {
it('returns data', async () => {
const data = await getGroups();
console.log('DATA', data); <---- UNDEFINED?
expect(data).toBeDefined();
expect(data).toMatchObject(fakeRespose);
});
it('handles error', async () => {
// const data = await getGroups();
await getGroups().toThrow('Failed');
});
});

What are you doing wrong here?
Default export in your mock instead of named as in the implementation
In your implementation you're using named export and you're importing { getGroups } so to make it work you need to change your mock like this
__mocks__\getGroups.js
export const getGroups = async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
working example
TL;DR
Testing mock
There is no point at all to test mock function. This does not proves your implementation is working. Even if you change your implementation your tests will still pass.
Use mocks only for the dependencies of your implementation
Use jest.genMockFromModule
It will create jest.fn() for each of the module's exported methods and will preserve the constants, allowing you to change the return value/implementation for some test cases and will also be able to write assertions if the function have been called
__mocks__\getGroups.js
const mock = jest.genMockFromModule('../getGroups');
mock.getGroups.mockResolvedValue({
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
})
module.exports = mock;
Jest will automatically hoist jest.mock calls (read more...)
So you can safely leave the import statements first and then call jest.mock

From Jest Docs, here's an example of a Mock.
jest.mock('../moduleName', () => {
return jest.fn(() => 42);
});
// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';
In your case data is undefined, because you haven't actually supplied a mocked implementation for the function or the mock hasn't worked and you're still calling the original function.
Example Reference: https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options
However, in your simple case you could also solve this with a spy, either jest.spyOn or jest.fn(). Here are two solutions to what you're trying to achieve. You can look at the code and run it here: https://repl.it/repls/FairUnsungMice
UPDATE after comment:
Manual mocks are defined by writing a module in a __mocks__/
subdirectory immediately adjacent to the module. For example, to mock
a module called user in the models directory, create a file called
user.js and put it in the models/__mocks__ directory. Note that the
__mocks__ folder is case-sensitive, so naming the directory __MOCKS__ will break on some systems.
Double check the naming, directory structure & type of exports you've setup - they should match. Also, it's worth checking this out: https://github.com/facebook/jest/issues/6127 - looks like an open issue with jest. If you need a solution, look at using a different approach as I mentioned.
Reference: https://jestjs.io/docs/en/manual-mocks

Related

How to use jest.spyOn for a function (React)

How do I write a test for this function? I first tried to include an optional param in the function such as mockData and go from there, but that seems too tedious. I have a function, something like this:
export async function getBreadcrumbs(service, id) {
const { folderId, label } = await getFileDetailsById(id);
const { breadcrumbs } = await getFoldersService(service, folderId);
return [...breadcrumbs, { label, id: '' }];
}
I'm thinking jest.spyOn is what I need to write the test with but I'm not sure how to approach it. I started with:
import * as crumbs from '.....data'
describe('getCrumbs', () => {
it('should return breadcrumbs name and id', async () => {
const data = jest.spyOn(crumbs, 'getBreadcrumbs')
});
});
But I'm not sure where to go from here. How can I mock a return of this function? This function returns:
[{ displayName: 'Hey', id: '' }]
getFileDetailsById and getFoldersService are API calls.
TLDR: I have a function and I'm unsure how to mock its return.

Mocked API response is `undefined` in jest with vue-utils

I've got a Vue 3 app which makes an API call and processes the response for adjusting some data fields. The API call is made after the child component emits a custom event, which the parent is listening on. Its working fine, but now I want to test (jest 26, vue-test-utils) it and I'm failing all the time. I'm not able to mock the response, its always undefined. Therefore, i cannot expect the changed data fields.
I'm new to jest, but I've think this is the desired way to mock a response. Or am I wrong?
This is my abstracted test code. Have a look at the open()-method and how i try to mock its API response.
Structure:
-
|- Parent.vue
|- Child.vue
|- api.js
Parent.vue
<template>
<Child
v-for="price in prices"
:key="price.type"
:price="price"
#open="open"
></Child>
</template>
<script>
// imports
export default {
name: "Parent",
components: {
Child
},
props: ["id"],
async mounted() {
// on creation load initial prices
const pricesDto = await loadPricesFor(this.id);
this.prices = toPrices(pricesDto); // external dto to array mapper
},
data() {
return {
prices: []
};
},
methods: {
async open() {
try {
const response = await makeApiCall(this.id);
console.log("foo", response); // response is undefined!
// make something with response data
} catch (error) {
// do nothing
}
}
}
};
</script>
parent.test.js:
import {flushPromises, mount} from "#vue/test-utils";
import * as apiModule from "./api.js";
import Child from "./Child";
jest.mock("./api.js");
it("test api call", async () => {
// given
const wrapper = async () => {
return mount(Child, {
props: {
id: "123",
},
});
};
await flushPromises();
// when
wrapper.findComponent(Child).vm.$emit("open");
// then
const mockedResponse = {
foo: "bar",
};
const response = Promise.resolve({
status: 200,
ok: true,
json: () => Promise.resolve(mockedResponse),
});
const spy = jest.spyOn(apiModule, "makeApiCall").mockReturnValueOnce(response);
expect(spy).toHaveBeenCalledWith("123");
// expect changed data fields regarding to response. This is not working!
});
I hope you might solve it without a interactive example, if not tell me and I'm gonna code some.
Solution: Okay I was dumb. I need to mock my response before firing the custom event not afterwards (makes sense...). So the code works as expected if I'm moving
const spy = jest.spyOn(apiModule, "makeApiCall").mockReturnValueOnce(response);
before //when block.

Jest Received: serializes to the same string on Object

newbie testing here and I ran into this error
module.js
import reducer from './reducer';
export default function getGModalModule({ domain }) {
return {
id: `GModal-module-${domain}`,
reducerMap: {
[domain]: reducer({
domain,
}),
},
};
}
module.test.js
import getGModalModule from '../module';
import reducer from '../reducer';
describe('getGModalModule', () => {
it('returns the correct module', () => {
const reducers = reducer({
domain: 'demo'
})
const expectVal = getGModalModule({
domain: 'demo'
});
const received = {
id: `GModal-module-demo`,
reducerMap: {
demo: reducers,
},
}
console.log("expectVal", expectVal)
console.log("received", received)
expect(expectVal).toEqual(received);
});
});
The error says something like this:
Expected: {"id": "GModal-module-demo", "reducerMap": {"demo": [Function gModalReducer]}}
Received: serializes to the same string
I've tried to log out the result and they look exactly the same, any idea why this doesn't pass the test ?
expectVal { id: 'GModal-module-demo', reducerMap: { demo: [Function: gModalReducer] } }
received { id: 'GModal-module-demo', reducerMap: { demo: [Function: gModalReducer] } }
Thanks in advance,
Jest cannot compare functions. Even if they are serialized into the same string, even if their source code is the same, function may refer to different variables through closures or bound to different this so we cannot say if functions are equal.
expect(function aaa() {}).toEqual(function aaa() {}); // fails
You may want to exclude methods before comparison explicitly or create custom matcher that will do that for you. But much better if you test what functions does instead. In your case you probably have separate test for ./reducer.js, right? You can just join both test: that one testing reducer in isolation and this one that checks object key names.

Jest mock function inside function

I have no idea how to mock return value of inner function inside jest
I tried different approaches. Finally I found this answer
but value not mocking for some reason, here it is example:
countries.js
export const countryList = () => [
{
label: '+244',
value: 'Angola',
}, // list of all possible countries very long...
];
export const getSortedCountryData = intlLang =>
countriesList()
.sort((compare, comparable) =>
compare.value.localeCompare(comparable.value, intlLang, { sensitivity: 'base' }));
countries.test.js
import * as countyListHelper from './countries';
describe('countries list', () => {
test('returns list of countries', () => {
const mockFn = jest.mock();
const expectedList = [
{
label: '+244',
value: 'Angola',
},
{
label: '+43',
value: 'Austria',
},
];
mockFn.spyOn(countyListHelper, 'countriesList').mockReturnValue(expectedList);
// console.log('if return value mocked correctly',
// countyListHelper.countriesList() === expectedList); // true
expect(countyListHelper.getSortedCountryData('en')).toEqual(expectedList);
// shows error with received value list of all countries instead of mocked one
});
});
Please suggest me any possible solutions why inside test function mocked return value for inner function is ignored. Setup from Create React App.
The question you linked to has a currently accepted answer that doesn't work. I added a new answer with an explanation and working example.
The same concept applies here: a mock replaces the module export of a function so to be able to mock countriesList within getSortedCountryData you have to call the module export for countriesList.
One option is to move countriesList to its own module.
The other option is to take advantage of the fact that "ES6 modules support cyclic dependencies automatically" so it is perfectly valid to import the module into itself so that you can call the module export for countriesList:
countries.js
import * as countyListHelper from './countries';
export const countriesList = () => [
{
label: '+244',
value: 'Angola',
}, // list of all possible countries very long...
];
export const getSortedCountryData = intlLang =>
countyListHelper.countriesList()
.sort((compare, comparable) =>
compare.value.localeCompare(comparable.value, intlLang, { sensitivity: 'base' }));
countries.test.js
import * as countyListHelper from './countries';
describe('countries list', () => {
test('returns list of countries', () => {
const expectedList = [
{
label: '+244',
value: 'Angola',
},
{
label: '+43',
value: 'Austria',
},
];
const spy = jest.spyOn(countyListHelper, 'countriesList');
spy.mockReturnValue(expectedList);
expect(countyListHelper.getSortedCountryData('en')).toEqual(expectedList); // Success!
spy.mockRestore();
});
});

Unit test Vuex getters that depend on other getters

I've manage to test Vuex getters that are isolated from other code. I'm now facing some issues when a getter depends on other getters, see the following example:
getters.js
export const getters = {
getFoo(state) => prefix {
return `${prefix}: ${state.name}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
getters.spec.js
import { getters } = './getters';
const state = {
name: 'stackoverflow'
};
describe('getFoo', () => {
it('return name with prefix', () => {
expect(getters.getFoo(state)('name')).toBe('name: stackoverflow');
});
});
describe('getFancyNames', () => {
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, _getters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
]);
});
});
When the tested getter depends on other getter that has arguments this means that I've reference the original getter.getFoo on the mock, and this breaks the idea of mocking, since the tests start to have relation with each other. When the getters grow, and the dependency graph has several levels it makes the tests complex.
Maybe this is the way to go, just wanted to check that I'm not missing anything...
I agree with you that referencing the actual collaborator in your mock defeats the purpose of a mock. So instead I would simply directly return whatever you want your collaborator to return.
In your example, instead of doing something like this:
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
You would simply put in whatever getters.getFoo(state) would return:
const _getters = {
getFoo: 'foobar'
}
If you have a getter that takes an additional argument you would simply return a function that returns a constant:
const _getters = {
getFoo: x => 'foobar',
}
Since I'm using Jest there is an option in the jest mock function that let's specify the return value when called:
mockReturnValueOnce or mockReturnValue
More information can be found here: https://facebook.github.io/jest/docs/en/mock-functions.html#mock-return-values
Using the same code as in the question this could be solved like this:
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const getFoo = jest.fn()
getFoo.mockReturnValueOnce('foo: stackoverflow')
getFoo.mockReturnValueOnce('bar: stackoverflow')
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, { getFoo })).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
A cleaner way that I have found is to create your own mocked getters object. This only works if the getter uses the unaltered state like the question does.
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, mockedGetters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
Extra
In the case that you do need to call other getter functions just pass the mocked getters objects into another mocked getters object. It sounds worse than is actually is.
getters.py
export const getters = {
getBar(state) = { // new extra hard part!
return state.bar,
},
getFoo(state, getters) => prefix {
return `${prefix}: ${state.name} with some ${getters.getBar}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
const _mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
const mockedGetters = {
.._mockedGetters, // Use the mocked object!
getBar: getters.getBar(state, _mockedGetters), // We only overwrite what is needed
};
// continue down the line as needed!

Categories