I have a mock file of my api.js located in __mocks__/api.js with default implementation like this:
export function api() {
return {
systemInfo: jest.fn(() => Promise.resolve({ system_info: { mi_workers_count: 3 } }))
}
}
however I want to change this default behaviour in one test, so I've tried to do it following way which doesn't work:
import { api as mockApi } from "__mocks__/api" //tried also from original impl. file "api"
test("something", async () => {
console.log(mockApi().systemInfo.getMockImplementation()())
mockApi().systemInfo.mockImplementationOnce(() =>
Promise.resolve({ system_info: { mi_workers_count: 1 } })
)
console.log(mockApi().systemInfo.getMockImplementation()())
})
both console.log will return same, origin promise response implemented in __mocks__/api
In order to do this, a spy should be accessible outside the scope it's defined. Since it's defined inside api, this cannot be done, mockApi().systemInfo !== mockApi().systemInfo.
The spy can be exported separately:
export const mockSystemInfo = jest.fn(() => Promise.resolve({ system_info: { mi_workers_count: 3 } }));
export function api() {
return {
systemInfo: mockSystemInfo
}
}
The implementation can be changed with:
mockSystemInfo.mockImplementationOnce(...)
Related
Trying to write a test to provide code coverage for the following code :
note : there are other functions in the service but just listing one for brevity.
export const service = {
getById: async (id) => {
const url = `/api/customers/${id}/names`
const {data} = await axios.get(url, axiosOptions);
return data;
}
I'm attempting to simply provide code coverage with this test:
note : I have attempted to use require instead of import but that does not seem to work.
import {service} from './requests';
it("mocks the getById function", () => {
service.getById = jest.fn();
expect(service.getById.mock).toBeTruthy();
}
This test passes however seems to provide no code coverage.
I've attempted to mock out the axios call but I seem to get nowhere as examples I've found of implementations are not working for me currently.
Does anyone have ideas and an example how I could provide code coverage for the service please?
Update : to sonEtLumiere's answer
jest.mock('./service', () => ({
getById: jest.fn().mockResolvedValue({ data : "hello"}),
}));
describe('test', () => {
it('mocks the service", async () => {
service.getById.mockResolvedValue({data: "hello});
const data = await service.getById(1);
expect(data).toEqual({data:"hello"});
})
})
Currently getting back error :
Cannot read properties of undefined (reading 'getById')
Any thoughts on why I'm getting this error?
To mock a service using Jest, you can use the jest.mock() function to create a mocked version of the service. For example:
jest.mock('path/to/service', () => ({
getById: jest.fn().mockResolvedValue({ /* mocked data */ }),
}));
Then, in your test file, you can import the mocked version of the service and use the mock property on the function to control its behavior. For example, you can use .mockResolvedValue to set the resolved value of the function, or use .mockRejectedValue to make the function throw an error.
import { service } from 'path/to/service';
describe('test', () => {
it('mocks the service', async () => {
service.getById.mockResolvedValue({ /* mocked data */ });
const data = await service.getById(1);
expect(data).toEqual({ /* mocked data */ });
});
});
I do agree with #Lin Du's comment, if you want to test service.getById, you should be mocking what the method depends on, in this case axios.get.
But following along with your question, the issue is that the named export in ./requests is an object containing the getById property which is the method you want to test. So jest.mock should look like:
jest.mock("./requests.js", () => ({
service: {
getById: jest.fn(),
},
}))
Then your test will pass as you expected:
it("mocks the getById function", async () => {
service.getById.mockResolvedValueOnce({ data: "hello" })
const data = await service.getById(1)
expect(data).toEqual({ data: "hello" })
})
But again, if you want to test a method and have proper coverage, what you need to mock is the method's dependency, not the method itself, e.g:
import { service } from "./requests"
import axios from "axios"
jest.mock("axios")
test("service.getById", async () => {
axios.get.mockResolvedValueOnce({ data: "hello" })
const result = await service.getById(1)
expect(result).toBe("hello")
})
Is it possible to test startServer() call once launcApp is called?
import { Server } from './Server/Server';
export class Launcher {
private server: Server;
public constructor() {
this.server = new Server();
}
public launchApp() {
this.server.startServer();
}
}
I tried to do
Server.prototype.startServer = jest.fn();
new Launcher.launchApp();
expect(Server.prototype.startServer).toBeCalled();
but it fails
Your approach worked for me. The only problem with your snippet was that you are not instantiating your Launcher properly. It needs to be:
Server.prototype.startServer = jest.fn();
new Launcher(). launchApp();
expect(Server.prototype.startServer).toBeCalled();
But if you are looking for other ways to mock methods in classes, there are many to achieve it, see: Jest docs: The 4 ways to create an ES6 class mock. One of the ways is described under: https://jestjs.io/docs/es6-class-mocks#mocking-non-default-class-exports
So you can use jest.mock() with your module path, then return an object with the key that is the same as the class export name.
const mockStartServer = jest.fn();
jest.mock("./Server/Server", () => {
return {
Server: jest.fn().mockImplementation(() => {
return { startServer: mockStartServer };
}),
};
});
Note that mockStartServer is defined and initialized with jest.fn() on the outer scope, and outside the returned object, and that's because Jest's mock behavior:
"Since calls to jest.mock() are hoisted to the top of the file, Jest
prevents access to out-of-scope variables. By default, you cannot
first define a variable and then use it in the factory. Jest will
disable this check for variables that start with the word mock.
However, it is still up to you to guarantee that they will be
initialized on time. Be aware of Temporal Dead Zone". src: jest documentation
Then the test would be:
import { Launcher } from "./luncher";
import { Server } from "./Server/Server";
const mockStartServer = jest.fn();
jest.mock("./Server/Server", () => {
return {
Server: jest.fn().mockImplementation(() => {
return { startServer: mockStartServer };
}),
};
});
it("should check for server start", () => {
const luncher = new Launcher();
luncher.launchApp();
expect(mockStartServer).toHaveBeenCalled();
});
You can also create a mock with spyOn, by overriding the implementation of the spied Server.prototype.startServer method.
const startServerMock = jest
.spyOn(Server.prototype, "startServer")
.mockImplementation(() => {});
it("should check for server start", () => {
const lun = new Launcher();
lun.launchApp();
expect(startServerMock).toHaveBeenCalled();
});
I am trying to mock a method used by the class I'm testing. The method is located in the same file as the class I'm testing.
The tested file:
export function trackErrors() {
console.log("This SHOULD NOT BE SEEN ON tests");
// Currently this console log is visible hwne running tests
}
// Component that is tested
export const form = () => {
return <FormComponent callback={trackErrors} />
}
The test itself:
With mock
The jest.mock() is the first thing to happen, before the import.
jest.mock('./testedFile', () => {
const actualModule = jest.requireActual('./testedFile')
return new Proxy(actualModule, {
get: (target, property) => {
switch (property) {
case 'trackErrors': {
return jest.fn()
}
default: {
return target[property]
}
}
},
})
})
import * as SModule from "./testedFile";
it.only("Calls trackErrors", async () => {
const { getByTestId } = <SModule.form />;
await typeEmail(getByTestId, "abcd");
await waitFor(() => {
expect(SModule.trackErrors).toHaveBeenCalledTimes(1);
});
});
This does not work. When printing SModule.trackErrors, I can see the mocked function being put out. But when the test is executed, the test fails with 0 calls and I see this console.log This SHOULD NOT BE SEEN ON tests. That means that the method is mocked good inside the test, but when rendering the component it continues to call the prexistent method (the one not modked).
I've also tried the spyOn approach, but the outcome is exactly the same.
I have got a component which might not request an ajax call if some data has been passed into it. However if the data hasn't been passed in I need to fetch it, so I want to import Axios then, save importing it for no reason.
How can I wait for the script to be imported before attempting to use it, as the below doesn't work:
export default {
props: {
vehicleId: {
type: Number|String,
required: true,
default: () => null
},
settings: {
type: Object,
default: () => null
}
},
beforeCreate() {
if (!this.settings) {
const Axios = () => import('../../../axiosConfig');
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
}
},
Dynamic import return a Promise, so you must to use then function.
Try something like that:
<script>
export default {
beforeCreate() {
if (!this.settings) {
import('../../../axiosConfig').then(axios => {
axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
});
}
},
};
</script>
Avoid the approach with async/await because the lifecycle functions don't support asynchronous in Vue.js.
You're almost there, import() is async, so just do:
// or use .then if you're not in an async function
const Axios = (await import('../../../axiosConfig')).default
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
and notice that import() returns the module, so you need to get the .default property if you need the default export (like in your case) or just call .someExportedName for importing a named export (i.e. non-default export from the module)
I am trying to mock a function call, and expect it to have called another function within it once.
myFunctions.test.js
import { resetModal } from '../myFunctions.js';
describe('resetModal', () => {
it('calls the clearSomethingInModal function', () => {
const clearSomethingInModal = jest.fn();
resetModal();
expect(clearSomethingInModal.mock.calls.length).toBe(1);
})
})
myFunctions.js
export resetModal() {
clearSomethingInModal()
}
However, Jest output says that it has not been called. How can I best do this?
Your approach does not work because you mock clearSomethingInModal only in the context of your test file, so clearSomethingInModal in the myFunctions.js is still the original. The main point is that you can't mock something that is directly created in myFunctions.js. The only thing that you can mock are:
Modules that you import to myFunctions.js, like import clearSomethingInModal from 'clearSomethingInModal';
Callbacks that you pass into your function when calling them from your test;
This makes sense if you think about myFunctions.js as a blackbox, where you can control what goes in, like imports or function arguments, and where you can test what comes out. But you can not test the stuff that happens inside the box.
Here are two example that reflect the 2 points in the list:
myFunctions.test.js
import { resetModal } from '../myFunctions.js';
import clearSomethingInModal from 'clearSomethingInModal';
jest.mock('clearSomethingInModal', () => jest.fn())
describe('resetModal', () => {
it('calls the clearSomethingInModal function', () => {
resetCreationModal();
expect(clearSomethingInModal.mock.calls.length).toBe(1);
})
})
myFunctions.js
import clearSomethingInModal from 'clearSomethingInModal';
export resetModal() {
clearSomethingInModal()
}
myFunctions.test.js
import { resetModal } from '../myFunctions.js';
describe('resetModal', () => {
it('calls the clearSomethingInModal function', () => {
const clearSomethingInModal = jest.fn();
resetCreationModal(clearSomethingInModal);
expect(clearSomethingInModal.mock.calls.length).toBe(1);
})
})
myFunctions.js
export resetModal(clearSomethingInModal) {
clearSomethingInModal()
}
Another way is to use done and mock or spy on the implementation of the last function and check if the previous function was called by then.
it('should call function2 after function1', (done) => {
expect.assertions(2)
function2.mockImplementationOnce(() => {
expect(function1).toHaveBeenCalled()
done()
})
act() // This is where you run function you are testing
})
The drawback of this solution is that the structure of the test is not
// arrange
// act
// assert
but rather
// arrange & assert
// act