I have a typescript class userInfo.ts:
export class UserInfo {
public getName() {
return "I am real name";
}
}
I have a mocked class userInfo.ts in mocks folder:
export class UserInfo {
public getName() {
return "I am fake name";
}
}
I have a client:
import { UserInfo } from "./userInfo";
export class Client {
public functionToTest() {
let validation = new UserInfo();
return validation.getName();
}
}
And finally I want to have TWO tests for this, in the first one I want to overwrite the getName mock only for this test, and in the second one I want to have the mocked class behavior so:
import { Client } from "./client";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = UserInfo as jest.MockedClass<typeof UserInfo>; // I tried with this but with no success
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
// UserInfo.prototype.getName = jest.fn().mockImplementationOnce(() => {
// return "Something weird happened";
// });
userInfoMocked.prototype.getName = jest.fn().mockImplementationOnce(() => {
return "something weird happend";
});
// this is not working either
// Property 'getName' does not exist on type 'MockedClass<typeof UserInfo>'.
// userInfoMocked.getName = jest.fn().mockImplementationOnce(() => {
// return "something weird happend";
// });
let text = client.functionToTest();
expect(text).toBe('something weird happend');
let text2 = client.functionToTest();
expect(text2).toBe('I am fake name'); // I get undefined (I overwrote prototype!)
});
it('should get fake name now', () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe('I am fake name'); // I get undefined
});
});
I am suprised that such a common (I think) functionality is not achievable? How to succeed in this? Is this even possible?
You can assign a default implementation to the mock:
import userInfo from "./userInfo"
jest.mock("./userInfo", () => ({
getName: jest.fn(() => 'John Doe'),
}));
And each time you want to overwrite the implementation:
userInfo.getName.mockImplementation(() => jest.fn().mockReturnValue('another value'));
If you want to use different mocks in different tests, don't use the mocks folder. Instead create the mocks you need for each test. This describes the different types of mocking you can do. Based on your description I would use mockImplementation. eg in one test you could do
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a real name'
}
}
}
And in another test:
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a fake name'
}
}
}
All the methods boil down to the same thing so it's a question of picking the one that fits in best with the structure of your code and is easy to maintain.
Here is my solution to this:
It occurred that when not using manual mock (thus having UserInfo.ts under mocks) it does not work at all, but when getting rid of that manual mock and left automock, I could do:
import { mocked } from "ts-jest";
import { Client } from "./secretsManagerWrapper";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = mocked(UserInfo, false);
describe("Client", () => {
it.only("should get Name", () => {
let client = new Client();
userInfoMocked.mockImplementationOnce(function () {
return {
getName: () => {
return "John Doe Second";
},
};
});
and it worked !
I discovered also that when I do want to use manual mock (under mocks folder) I can use a Spy:
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
jest.spyOn(userInfoMocked.prototype, "getName").mockImplementationOnce(() => {
return "John Doe Second";
});
let text = client.functionToTest();
expect(text).toBe("John Doe Second");
let text2 = client.functionToTest();
expect(text2).toBe("I am fake name"); // I get what I want now
});
it("should get fake name now", () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe("I am fake name"); // I get what I want now !
});
});
And I can accomplish what I wanted :)
I have a jest test that is calling the real function and it compares the result returned with an expected result. The service function called uses uuid. I have all kind of errors while trying to mock uuid and can't seem to succeed.
My code is:
import uuid from 'uuid';
import tinyRuleset from './tiny_ruleset.json';
import { Store } from '../store';
describe('TuningStore test ', () => {
let store;
let db;
beforeEach(async () => {
db = levelup(encode(memdown(), { valueEncoding: 'json' }));
store= new Store(db);
});
test('createObject()', async () => {
jest.spyOn(uuid, 'v4').mockReturnValue('abc22');
const obj = await store.createObject();
expect(obj ).toEqual({
a: expect.any(string),
b: 'tiny_ruleset',
v: expect.any(Function)
});
});
})
I tried several ways, but none of them worked. My current error is: uuid is not a function. Also tried this:
const uuidv4Spy = jest.spyOn(store.$uuid, 'v4').mockReturnValueOnce('fake uuid');
Basically uuid is used inside the store.createObject() function.
Thank you!
As explained here Mock uuid
const uuidMock = jest.fn().mockImplementation(() => {
return 'my-none-unique-uuid';
});
jest.mock('uuid', () => {
return uuidMock;
});
you need to apply the mock in the test file before you are importing your real file.
I need to test a function which opens a new tab in the browser
openStatementsReport(contactIds) {
window.open(`a_url_${contactIds}`);
}
I would like to mock window's open function, so I can verify the correct URL is passed in to the open function.
Using Jest, I don't know how to mock window. I tried to set window.open with a mock function, but this way doesn't work. Below is the test case:
it('the correct URL is called', () => {
window.open = jest.fn();
statementService.openStatementsReport(111);
expect(window.open).toBeCalled();
});
But it gives me the error
expect(jest.fn())[.not].toBeCalled()
jest.fn() value must be a mock function or spy.
Received:
function: [Function anonymous]
What should I do to the test case?
The following method worked for me. This approach allowed me to test some code that should work both in the browser and in Node.js, as it allowed me to set window to undefined.
This was with Jest 24.8 (I believe):
let windowSpy;
beforeEach(() => {
windowSpy = jest.spyOn(window, "window", "get");
});
afterEach(() => {
windowSpy.mockRestore();
});
it('should return https://example.com', () => {
windowSpy.mockImplementation(() => ({
location: {
origin: "https://example.com"
}
}));
expect(window.location.origin).toEqual("https://example.com");
});
it('should be undefined.', () => {
windowSpy.mockImplementation(() => undefined);
expect(window).toBeUndefined();
});
Instead of window, use global:
it('the correct URL is called', () => {
global.open = jest.fn();
statementService.openStatementsReport(111);
expect(global.open).toBeCalled();
});
You could also try:
const open = jest.fn()
Object.defineProperty(window, 'open', open);
There are a couple of ways to mock globals in Jest:
Use the mockImplementation approach (the most Jest-like way), but it will work only for those variables which has some default implementation provided by jsdom. window.open is one of them:
test('it works', () => {
// Setup
const mockedOpen = jest.fn();
// Without making a copy, you will have a circular dependency problem
const originalWindow = { ...window };
const windowSpy = jest.spyOn(global, "window", "get");
windowSpy.mockImplementation(() => ({
...originalWindow, // In case you need other window properties to be in place
open: mockedOpen
}));
// Tests
statementService.openStatementsReport(111)
expect(mockedOpen).toBeCalled();
// Cleanup
windowSpy.mockRestore();
});
Assign the value directly to the global property. It is the most straightforward, but it may trigger error messages for some window variables, e.g. window.href.
test('it works', () => {
// Setup
const mockedOpen = jest.fn();
const originalOpen = window.open;
window.open = mockedOpen;
// Tests
statementService.openStatementsReport(111)
expect(mockedOpen).toBeCalled();
// Cleanup
window.open = originalOpen;
});
Don't use globals directly (requires a bit of refactoring)
Instead of using the global value directly, it might be cleaner to import it from another file, so mocking will became trivial with Jest.
File ./test.js
jest.mock('./fileWithGlobalValueExported.js');
import { windowOpen } from './fileWithGlobalValueExported.js';
import { statementService } from './testedFile.js';
// Tests
test('it works', () => {
statementService.openStatementsReport(111)
expect(windowOpen).toBeCalled();
});
File ./fileWithGlobalValueExported.js
export const windowOpen = window.open;
File ./testedFile.js
import { windowOpen } from './fileWithGlobalValueExported.js';
export const statementService = {
openStatementsReport(contactIds) {
windowOpen(`a_url_${contactIds}`);
}
}
I'm directly assigning jest.fn() to window.open.
window.open = jest.fn()
// ...code
expect(window.open).toHaveBeenCalledTimes(1)
expect(window.open).toHaveBeenCalledWith('/new-tab','_blank')
In my component I need access to window.location.search. This is what I did in the Jest test:
Object.defineProperty(global, "window", {
value: {
location: {
search: "test"
}
}
});
In case window properties must be different in different tests, we can put window mocking into a function, and make it writable in order to override for different tests:
function mockWindow(search, pathname) {
Object.defineProperty(global, "window", {
value: {
location: {
search,
pathname
}
},
writable: true
});
}
And reset after each test:
afterEach(() => {
delete global.window.location;
});
We can also define it using global in setupTests:
// File 'setupTests.js'
global.open = jest.fn()
And call it using global in the actual test:
// File 'yourtest.test.js'
it('the correct URL is called', () => {
statementService.openStatementsReport(111);
expect(global.open).toBeCalled();
});
I found an easy way to do it: delete and replace
describe('Test case', () => {
const { open } = window;
beforeAll(() => {
// Delete the existing
delete window.open;
// Replace with the custom value
window.open = jest.fn();
// Works for `location` too, eg:
// window.location = { origin: 'http://localhost:3100' };
});
afterAll(() => {
// Restore original
window.open = open;
});
it('correct url is called', () => {
statementService.openStatementsReport(111);
expect(window.open).toBeCalled(); // Happy happy, joy joy
});
});
The window object in Jest is self-mocking
One of the things unaddressed in other answers is a comment by the OP:
Using Jest, I don't know how to mock the window.
The window object is already mocked and can be referenced out of the box.
From the documentation:
Jest ships with jsdom which simulates a DOM environment as if you were in the browser. This means that every DOM API that we call can be observed in the same way it would be observed in a browser!
Example:
describe('i am a window', () => {
it('has a window object', () => {
expect(window).toBeTruthy(); // test will pass
});
});
You can try this:
import * as _Window from "jsdom/lib/jsdom/browser/Window";
window.open = jest.fn().mockImplementationOnce(() => {
return new _Window({ parsingMode: "html" });
});
it("correct url is called", () => {
statementService.openStatementsReport(111);
expect(window.open).toHaveBeenCalled();
});
If it's similar to the window location problem at window.location.href can't be changed in tests. #890, you could try (adjusted):
delete global.window.open;
global.window = Object.create(window);
global.window.open = jest.fn();
In your Jest configuration, add setupFilesAfterEnv: ["./setupTests.js"], create that file, and add the code you want to run before the tests:
// setupTests.js
window.crypto = {
.....
};
Reference: setupFilesAfterEnv [array]
Try simply:
let windowOpenSpy: jest.SpyInstance;
beforeEach(() => {
windowOpenSpy = jest.spyOn(window, 'open');
});
it('should open window with dashboard url', () => {
expect(windowOpenSpy).toBeCalledWith('your URL', '_blank');
});
I have a utility function which allows me to mock any method on the window like so:
function givenMockWindowMethods(methods: Partial<{ [key in keyof Window]: jest.Mock<any, any> }>): () => void {
const mocks = Object.values(methods);
Object.entries(methods).forEach(([key, value]) => {
Object.defineProperty(window, key, { value });
});
return (): void => mocks.forEach((mock) => mock?.mockClear());
}
So if I need to mock the open method (or anything really) on the window, I can do:
const cleanupMocks = givenMockWindowMethods({ open: jest.fn() });
// expect(...).toBe(...)
//at the end of the test, clean it up
cleanupMocks()
You can test it:
describe('TableItem Components', () => {
let open_url = ""
const { open } = window;
beforeAll(() => {
delete window.open;
window.open = (url) => { open_url = url };
});
afterAll(() => {
window.open = open;
});
test('string type', async () => {
wrapper.vm.openNewTab('http://example.com')
expect(open_url).toBe('http://example.com')
})
})
const windowSpy = jest.spyOn(iFrame, "contentWindow", "get");
windowSpy.mockImplementation(() => ({
location: {
origin: "https://test.com",
href: "href",
hash: "hash"
}
}));
I tried a similar test, and it worked with me...
My code:
export const Blah = () => {
const BLAH = 'https://www.google.com/'
const handleBlah = () => {
window.open(BLAH, '_blank')
}
return (
<button onClick={handleBlah}> BLAHBLAH </button>
)
}
My test using Jest:
it('should be able to render "BLAHBLAH " button ', () => {
window.open = jest.fn();
const BLAH = 'https://www.google.com/'
const { getByText } = render(<Blah/>) // Get text by my page Blah
const buttonGoToBlah = getByText('BLAHBLAH') // Get button by text
fireEvent.click(buttonGoToBlah) // Simulate the click event
expect(window.open).toHaveBeenCalledTimes(1) // Expect the window.open have to been called at least once.
expect(window.open).toHaveBeenCalledWith(BLAH, '_blank'); // And the page should be the same called in my BLAH page
})
I have a node module which exports a few classes, one of which is Client, which I use to create a client (having a few APIs as methods).
I'm trying to test my module which uses this node module as a dependency using Jest. However, I've been unable to successfully mock the one method (say search()) in the Client class.
Here is my spec for myModule:
//index.spec.ts
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
beforeAll(() => {
nock.disableNetConnect();
});
it('test search method in my module', () => {
jest.mock('node-module-name');
const mockedClient = <jest.Mock<externalModule.Client>>externalModule.Client;
const myClient = createClient({/*params*/}); //returns instance of Client class present in node module by executing Client() constructor
myClient.searchByName('abc'); //calls search API - I need to track calls to this API
expect(mockedClient).toHaveBeenCalled();
expect(mockedClient.prototype.search).toHaveBeenCalledWith('abc');
});
});
This, however, doesn't create a mock at all and triggers a nock error since the search API tries to connect to the url (given through params).
I've also tried mocking the Client class like the following. While successfully creating a mock for the Client class and also the search API (verified that search() is also mocked through console logs), it gives me an error while I try to check if search() has been called.
externalModule.Client = jest.fn(() => { return { search: jest.fn(() => Promise.resolve('some response')) } });
//creates the mock successfully, but not sure how to track calls to 'search' property
const client = myModule.createClient(/*params*/);
client.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled(); //Successful
expect(externalModule.Client.prototype.search).toHaveBeenCalled(); //returns error saying "jest.fn() value must be a mock function or spy, Received: undefined"
I'm not sure what I'm doing wrong. Thank you in advance.
Mocking whole module
Try moving jest.mock to the top of file
//index.spec.ts
const search = jest.fn();
jest.mock('node-module-name', () => ({
Client: jest.fn(() => ({ search }))
}));
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
beforeAll(() => {
nock.disableNetConnect();
});
it('test search method in my module', () => {
const myClient = createClient({/*params*/});
myClient.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalledWith('abc');
externalModule.Client.mockClear();
search.mockClear();
});
});
Mocking only Client
Create search constant and track it.
const search = jest.fn();
externalModule.Client = jest.fn(() => ({ search }));
const client = myModule.createClient(/*params*/);
client.searchByName('abc');
expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalled();
Here is how I mocked it. I had to change naming and removing some code to avoid exposing original source.
jest.mock('../foo-client', () => {
return { FooClient: () => ({ post: mockPost }) }
})
Full code.
// foo-client.ts
export class FooClient {
constructor(private config: any)
post() {}
}
// foo-service.ts
import { FooClient } from './foo-client'
export class FooLabelService {
private client: FooClient
constructor() {
this.client = new FooClient()
}
createPost() {
return this.client.post()
}
}
// foo-service-test.ts
import { FooService } from '../foo-service'
const mockPost = jest.fn()
jest.mock('../foo-client', () => {
return { FooClient: () => ({ post: mockPost }) }
})
describe('FooService', () => {
let fooService: FooService
beforeEach(() => {
jest.resetAllMocks()
fooService = new FooService()
})
it('something should happened', () => {
mockPost.mockResolvedValue()
fooService.createPost()
})
})
I'm learning writing unit test with Jest.
I use typescript, but it shouldn't be a problem here. Feel free to provide examples with pure JavaScript.
Until now I have function:
const space = String.fromCharCode(0x0020);
const rocket = String.fromCharCode(0xD83D, 0xDE80);
let notified: boolean = false;
export const logHiring = (message: string = "We're hiring!", emoji: string = rocket) => {
if (!notified) {
console.info(
[message, emoji]
.filter((e) => e)
.join(space)
);
notified = true;
}
};
Yes, function should log to console just one message per initialization.
And not really working tests:
import {logHiring} from "../index";
const rocket = String.fromCharCode(0xD83D, 0xDE80);
// First test
test("`logHiring` without arguments", () => {
let result = logHiring();
expect(result).toBe(`We're hiring! ${rocket}`);
});
// Second test
test("`logHiring` with custom message", () => {
let result = logHiring("We are looking for employees");
expect(result).toBe(`We are looking for employees ${rocket}`);
});
// Third test
test("`logHiring` multiple times without arguments", () => {
let result = logHiring();
result = logHiring();
result = logHiring();
expect(result).toBe(`We're hiring! ${rocket}`);
});
I have two problems:
How can I test console logs? I've tried spyOn without succes.
How can I reset internal (from function) notified variable for each test?
How can I test console logs? I've tried spyOn without succes.
https://facebook.github.io/jest/docs/en/jest-object.html#jestspyonobject-methodname
const spy = jest.spyOn(console, 'log')
logHiring();
expect(spy).toHaveBeenCalledWith("We're hiring!")
How can I reset internal (from function) notified variable for each test?
export a getter/setter function like
// index.js
export const setNotified = (val) => { notified = val }
export const getNotified = _ => notified
// index.test.js
import { getNotified, setNotified } from '..'
let origNotified = getNotified()
beforeAll(_ => {
setNotified(/* some fake value here */)
...
}
afterAll(_ => {
setNotified(origNotified)
...
}