I'm mocking a ES6 class which is used inside my Vue Component:
export default class DataUploadApi {
// Get uploaded files
static async getUploadedFiles() : Promise<Object> {
return WebapiBase.getAsync({uri: DATA_UPLOAD_ENPOINTS.FILES});
}
}
I've been following along with this document, but I think my syntax is slightly off with my mock:
import { mount } from '#vue/test-utils';
import DataUploadApi from '../webapi/DataUploadService';
import FileDownloadList from '../components/file-download-list.vue';
const mockGetUploadedFiles = jest.fn().mockResolvedValue({json: JSON.stringify(uploadedFilesObj)});
jest.mock('../webapi/DataUploadService', () => jest.fn().mockImplementation(() => ({getUploadedFiles: mockGetUploadedFiles})));
describe('file-download-list component', () => {
beforeEach(() => {
// #ts-ignore
DataUploadApi.mockClear(); // https://stackoverflow.com/a/52707663/1695437 dont use # imports on mocks.
mockGetUploadedFiles.mockClear();
});
describe('renders correct markup:', () => {
it('without any uploaded files', () => {
const wrapper = mount(FileDownloadList, {});
expect(wrapper).toMatchSnapshot();
});
});
});
This test passes. However, in the snapshot I can see that the API called failed with this error message:
<p>
_DataUploadService.default.getUploadedFiles is not a function
</p>
What have I done wrong with the function mock? Thanks in advance!
There seemed to be a few issues with my code:
Mocking the API
Using an internal mockImplementation seems to cause issues, and is not required if you don't need the additional mock functionality.
jest.mock('#/apps/gb-data/webapi/DataUploadService', () => ({
getUploadedFiles() {
return Promise.resolve({ uploaded_files: {} });
},
}));
Changes to the test
Both flushPromises and nextTick are required.
it('with uploaded files', async () => {
const wrapper = mount(FileDownloadList, {
stubs: fileDownloadListStubs,
});
await flushPromises();
await wrapper.vm.$nextTick();
expect(wrapper).toMatchSnapshot();
});
Related
The following is an abstraction of my problem and thus does not make too much sense:
Given I have a simple utility callMethodIf that's returning the return of another imported method (blackbox).
~~/utils/call-method-if.js:
import { blackbox } from '~~/utils/blackbox';
export const callMethodIf = (condition) => {
return blackbox(condition);
};
~~/utils/blackbox.js:
export const blackbox = (condition) => {
return { called: condition };
};
How would I run one test case which calls the actual implementation of blackbox() and another one where I mock the return value of blackbox()?
I tried to do it that way:
import { describe, expect, it } from 'vitest';
import { callMethodIf } from '~~/utils/call-method-if';
describe('Call method if', () => {
it('returns "called: true" if condition is true', () => {
const result = callMethodIf(true);
expect(result).toEqual({ called: true });
});
it('returns mocked blackbox return object', () => {
vi.mock('~~/utils/blackbox', () => ({
blackbox: vi.fn().mockReturnValue({ mock: true })
}));
const result = callMethodIf(false);
expect(result).toEqual({ mock: true });
});
});
Both tests work if I run only one of them, but they don't work when combined.
Running vi.clearAllMocks() or vi.resetAllMocks() don't help.
Defining a global mock and overwriting it in my first test doesn't work either:
import { describe, expect, it } from 'vitest';
import { callMethodIf } from '~~/utils/call-method-if';
vi.mock('~~/utils/blackbox', () => ({
blackbox: vi.fn().mockReturnValue({ mock: true })
}));
describe('Call method if', () => {
it('returns "called: true" if condition is true', () => {
vi.mock('~~/utils/blackbox', async () => ({
blackbox: (await vi.importActual('~~/utils/blackbox')).blackbox
}));
const result = callMethodIf(true);
expect(result).toEqual({ called: true });
});
it('returns mocked blackbox return object', () => {
const result = callMethodIf(false);
expect(result).toEqual({ mock: true });
});
});
Okay, after lots of trial and error I finally got it to work. I can't really tell why my previous tries do not work tough.
Working solution:
import { describe, expect, it } from 'vitest';
import { callMethodIf } from '~~/utils/call-method-if';
vi.mock('~~/utils/blackbox');
describe('Call method if', () => {
it('returns "called: true" if condition is true', async () => {
const blackbox = await import('~~/utils/blackbox');
blackbox.blackbox = (await vi.importActual('~~/utils/blackbox')).blackbox;
const result = callMethodIf(true);
expect(result).toEqual({ called: true });
});
it('returns mocked blackbox return object', async () => {
const blackbox = await import('~~/utils/blackbox');
blackbox.blackbox = vi.fn().mockReturnValue({ mock: true });
const result = callMethodIf(false);
expect(result).toEqual({ mock: true });
});
});
When using TypeScript consider typing the importActual() return like that:
blackbox.blackbox = (await vi.importActual<typeof import('~~/utils/blackbox')>('~~/utils/blackbox')).blackbox;
I also ran in to this problem with using Vite. After a lot of trail and error I have managed to get the mocking of a module function working by using the following code:
vi.mock('#/models/generated/graphql')
describe('MyComponent works as expected', () => {
it('Shows loading when loading', async () => {
const graphql = await import('#/models/generated/graphql')
graphql.useGetAllQuery = vi.fn().mockReturnValue({ loading: true, error: null, data: null })
render(<MyComponent />)
expect(screen.findByTestId('my-component-loading')).toBeTruthy()
})
}
The function that I am mocking is this one (which is auto generated for my Graphql service):
export function useGetAllQuery(baseOptions?: Apollo.QueryHookOptions<GetAllQuery, GetAllQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetAllQuery, GetAllQueryVariables>(GetAllDocument, options);
}
I do not really understand why this works, but it does. I hope that this code snippet might help some.
I have this very simple function, and I must write a test. The goal is to fulfill the coverage threshold.
import { lambdaPromise } from '#helpers';
export const main = async event => lambdaPromise(event, findUsers);
The lambdaPromise() function returns a Promise. I am trying to mock it, then tell if it was called. Here's what I have:
import { main, findUsers } from '../src/lambdas/user/findUsers';
import { lambdaPromise } from '#helpers';
const mockEvent = {
arguments: {
userDataQuery: {
email: 'johndoe#whatever.com'
}
}
};
const mockLambdaPromise = jest.fn();
jest.mock('#helpers', () => ({
lambdaPromise: jest.fn().mockImplementation(() => mockLambdaPromise)
}));
describe('findUsers', () => {
it('should have a main function', async () => {
const mockPromise = main(mockEvent);
expect(mockPromise).toBeInstanceOf(Promise);
expect(mockLambdaPromise).toBeCalledWith(mockEvent, findUsers);
});
});
Now mockLambdaPromise never gets called. How to fix that?
Your mock returns a function, but you didn't call that function. The following makes it pass.
jest.mock("./helpers", () => ({
lambdaPromise: jest
.fn()
.mockImplementation((a, b) => mockLambdaPromise(a, b)),
}));
The complexity of that mock can be reduced by just mocking the resolved value with a spy:
import { main, findUsers } from "./findUsers";
import * as helpers from "./helpers";
describe("findUsers", () => {
it("should have a main function", async () => {
const spy = jest.spyOn(helpers, "lambdaPromise").mockResolvedValue();
await main(mockEvent);
expect(spy).toBeCalledWith(mockEvent, findUsers);
});
});
I want to mock the function forgotPassword inside the module authenticationPlugin/App, So i am doing this
jest.mock("authenticationPlugin/App", () => ({
forgotPassword (email: string) {
const msg='success'
email='a'
return msg
}
}))
Now i want to clear the mock of authenticationPlugin/App and have a different implementation for the forgotPassword method
So i did this
jest.clearAllMocks();
jest.mock("authenticationPlugin/App", () => ({
forgotPassword (email: string) {
throw new Error(<any>{'err':{'message':'Network Error'}})
}
}))
Now i expect the forgotPassword method to have a different implementation after clearing the mocks in for the module authenticationPlugin/App but it doesn't change...
If you want to have a different implementation for the mock in each test, you can use jest.fn instead.
Expanding on your code, it could look like this:
it('returns success', () => {
authApp.forgotPassword = jest.fn((email: string) => {
const msg='success'
email='a'
return msg
});
// Your test code here.
});
test('returns error', () => {
authApp.forgotPassword = jest.fn((email: string) => {
throw new Error(<any>{'err':{'message':'Network Error'}})
});
// Your test code here.
});
I have been trying to write tests to test a axios call but now need to test the catch part.
I have been able to do the then by mocking axios like so but can't seem to get a way to test catch. I have followed many different examples from stack overflow and the web.
jest.mock('axios', () => jest.fn(() => Promise.resolve({ data: mockData })));
but that will always return a good result so can't test the catch. The bit of code I want to test is: goToUrl() is just a window.location.assign(url) but imported.
fetchBundlesFromApi(params)
.then(({ data: { bundles } }) => {
updateBundles(bundles);
this.setState({ showUpdatingPrices: false });
})
.catch(() => goToUrl(bundlesUrl));
In my test for .then() part I do this:
const fetchedBundles = await fetchBundlesFromApi(
'?params',
);
expect(fetchedBundles.data).toEqual(mockData);
However if I follow examples like this one Mocking Axios with Jest in React - mock function not being called I can't manually mock get if I put a mock axios file in a folder __mocks__ then a lot of the test suit fails so I just want to mock it in this one test file.
here is one of the examples I tried doing:
jest.mock('axios', () => ({
get: () => jest.fn(() => Promise.resolve({ data: mockData })),
default: () => jest.fn(() => Promise.resolve({ data: mockData })),
}));
but the tests error with TypeError: (0 , _axios.default) is not a function
EDIT:
Here is my fetchBundlesApi function:
const fetchBundlesFromApi = params => axios(`${bundleRoute}/bundles${params}`);
EDIT: catch test
it('should redirect if api fails', async () => {
const networkError = new Error('Some network error');
axios.mockRejectedValueOnce(networkError);
const goToUrl = jest.fn();
let error;
try {
await fetchBundlesFromApi('?params');
} catch (err) {
error = err;
}
expect(error).toEqual(networkError);
expect(goToUrl).toHaveBeenCalled();
});
in my component I import goToUrl like so:
import { goToUrl } from 'Helpers';
You can make use of Jests ability to pop implementations off once they've run i.e. mockImplementationOnce and friends.
import axios from 'axios';
jest.mock('axios');
// default implementation
axios.get.mockResolvedValue(mockedData);
describe('#fetchBundlesFromApi', () => {
it('returns data from API', async () => {
const fetchedBundles = await fetchBundlesFromApi('?params');
expect(fetchedBundles.data).toEqual(mockData);
});
it('redirects on failure', () => {
// override behaviour for this one call
axios.get.mockRejectedValueOnce();
// verify your failure test
});
});
Let's say I have the following two files:
// index.js
...
import { IS_IOS } from 'common/constants/platform';
...
export const myFunction = () => (IS_IOS ? 'foo' : 'bar');
// index.test.js
...
import { myFunction } from './index';
jest.mock('common/constants/platform', () => ({ IS_IOS: true }));
describe('My test', () => {
it('tests behavior on IOS', () => {
expect(myFunction()).toBe('foo');
});
// --> Here I want to change the value of IS_IOS to false
it('tests behavior if NOT IOS', () => {
expect(myFunction()).toBe('bar');
});
});
As you see my mocking function returns IS_IOS: true. I want it to return IS_IOS: false after my first test. How would I do that?
I also tried an adaptation of the solution here but I couldn't get it work, because there the mock returns a function:
module.exports = {
foo: jest.genMockFunction();
}
whereas my mock should return a boolean value which is not called inside the file I'm testing.
That's what I did here:
// common/constants/__mock__/platform
export const setIsIos = jest.fn(val => (IS_IOS = val));
export let IS_IOS;
// index.test.js
...
import { IS_IOS, setIsIos } from 'common/constants/platform';
jest.mock('common/constants/platform');
describe('My test', () => {
setIsIos('foo');
it('tests behavior on IOS', () => {
expect(myFunction()).toBe('foo');
});
setIsIos('bar');
it('tests behavior if NOT IOS', () => {
expect(myFunction()).toBe('bar');
});
});
Oddly when console-logging, i.e. console.log(IS_IOS); I get the expected values. The test however seems to use the original value, i.e. undefined.
Add jest.resetModules() to the beforeEach() call of that describe() test suite:
describe('EventManager', () => {
beforeEach(() => {
jest.resetModules();
});
...
Additionally I found A more complete example on how to mock modules with jest here