Several mocks of one module in one file with jest - javascript

I have to functions for example a, b. Both of them are elements of one module, and they are re-exported in index.js. Function a invokes function b.
It all works if i use jest.mock on the top of the file, but if i want to specify different mock implementation of b function in every it block it doesn't work. Also i try'ed to use jest.doMock but it doesn't work as well.
a.js
import * as fromDependencies from '.'
export function(arg) {
return !fromDependencies && !arg;
}
b.js
export function b() {
//some code
return boolean;
}
index.js
export * from 'a.js',
export * from 'b.js'
testFile
import a from '../a.js';
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
jest.mock('../.', () => ({
b: jest.fn(() => true);
}))
expect(a(true)).toBe(false);
});
it('should return true', function() {
jest.mock('../.', () => ({
b: jest.fn(() => false);
}))
expect(a(false)).toBe(false);
});
});
The results are fake, anyway i want to call my mocked function not the original one. When i have jest.mock in the top of the file it works but i can achieve just one mock for on file. Do mock does't work. I'll be really grateful if somebody can provide some example how i can resolve that ;).

You could use a spy instead of a mock. Since a exports a function instead of an object then you'd have to wrap it.
import a from '../a.js'
const aWrapped = { a }
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
const mockA = jest.fn()
const spyedA jest.spyOn(aWrapped, a).mockReturnValue(mockA)
expect(spyedA(true)).toBe(false);
});
});
Something like that.

Related

How to only mock

jest unit test question here. If I am testing function A, and function A uses function B,
import { funcB } = require('./FileB');
const funcA = () => {
const someData = funcB();
}
Does this mean that, in my unit test file testing funcA, in order to mock funcB, do i HAVE to import the function to mock it? which means the file wouldn't use that function but simply there for mocking purposes or do I have to mock the entire module in which funcB lives on and mock the implementation of that single function?
my goal here is, I dont want every single test in my unit test file to have funcB mocked, I only want it mocked on some test but not all while testing funcA.
So if i have 4 test and 3 of them need it mocked but 2 need its actuall implementation, whats a best way to approach this?
One possible solution - use spyOn and mockRestore for following implementation:
export const funcB = () => {
console.log('original implementation of B')
return 42;
}
import { funcB } from './fileb';
export const funcA = () => {
const someData = funcB();
return someData;
}
import * as fileB from "../funcab/fileb";
import { funcA } from "../funcab/filea";
describe('testing 1...2...3...', () => {
const spy = jest.spyOn(fileB, 'funcB');
afterEach(() => {
spy.mockRestore();
})
it('will test with mocked funcB', () => {
spy.mockReturnValue(1);
expect(funcA()).toEqual(1);
});
it('will test without mocked funcB', () => {
expect(funcA()).toEqual(42);
})
})
Here is a post about Jest mock and spy — mockClear vs mockReset vs mockRestore. I hope you'll find it usefull.
Answer for comment - do you mean sth like this?
const mockFuncB = jest.fn();
jest.mock("../funcab/fileb", () => ({
funcB: () => mockFuncB(),
}));
then the test would look like:
it('will test with mocked funcB', () => {
mockFuncB.mockReturnValue(1);
expect(funcA()).toEqual(1);
});

Jest mocking inside the same module

I'm following this thread. Mock a function called by a tested function of the same file with jest
functions.js
export const a = (x) => { a very complicated function };
export const b = (x) => exports.a(x+1);
functions.test.js
import * as functions from './functions';
describe('b', () => {
test('calling b calls a with x+1', () => {
functions.a = jest.fn();
functions.b(1);
expect(functions.a).toHaveBeenCalledWith(2);
});
});
It works for the most part, except that if I have additional unit test after describe(b), that required the original implementation of a(), it will still be treated as mock function, like let's say I want to unit test a(), it won't work because it is an empty function now. e.g
describe('c', () => {
test('unit testing a will call something 3 times', () => {
functions.a()
expect(whatever.get).toHaveBeenCalledWith(3);
});
});
any way to fix this?
Note:
I have tried the following, but it does not work
beforeEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
The only way I can make it work is this, but this seems like really hacky?
const originalA = functions.a.bind({});
afterEach(() => {
functions.a = originalA;
});
Have you tried using spyOn? It doesn't replace your implementation just chekcing what is happening with that function.
import * as functions from "../functions";
describe("b", () => {
test("calling b calls a with x+1", () => {
const functionASpy = jest.spyOn(functions, 'a');
functions.b(1);
expect(functionASpy).toHaveBeenCalledWith(2);
});
});

Unable to mock a dependent function if its from the same module

I have been trying to debug why this is happening. I am unable to mock a dependent function if its from the same module as the function calling it. But I am able to overcome this if the mocked function is moved to a separate module which is different from the module of the function calling it.
Not working scenario
Module A (filename.ts)
export const callingFunction = () => {
//....statements
dependentFunction();
}
export const dependantFunction = () => {
//....statements
//resolve with something
}
filename.test.ts
import { callingFunction } from './fileName'
jest.mock('./fileName',() => ({
...jest.requireActuals('./fileName'),
dependentFunction: jest.fn().mockImplementation(/*....Mocked implementation*/)
})
test('...test case description...', () => {
const callingFunctionRespose: any = callingFunction();
expect(callingFunctionResponse).toEqual(/*....something.....*/);
});
The above mock does not override the dependentFunction exported by the fileName.ts module. Instead, when the exported function callingFunction() is called, it uses the implementation defined in the module. (Found this out by logging the function definitions.
But this behaviour is not observed when the dependant function is moved to it own separate module.
Working scenario
fileName.ts
import { dependentFunction } from './dependentFunctions'
export const callingFunction = () => {
//....statements
dependentFunction();
}
dependentFunctions.ts
export const dependantFunction = () => {
//....statements
//resolve with something
}
fileName.test.ts
import { callingFunction } from './fileName'
jest.mock('./dependentFunctions',() => ({
...jest.requireActuals('./dependentFunctions'),
dependentFunction: jest.fn().mockImplementation(/*....Mocked implementation*/)
})
test('...test case description...', () => {
const callingFunctionRespose: any = callingFunction();
expect(callingFunctionResponse).toEqual(/*....something.....*/);
});
You can import the functions as a module
import * as module from './fileName'
To mock the implementation you can do
jest.spyOn(module, 'dependentFunction').mockImplementation(/*....Mocked implementation*/)
To call the other function, use
module.callingFunction()
Appologies for the late update. But the solution in the following article was the fix:
https://medium.com/welldone-software/jest-how-to-mock-a-function-call-inside-a-module-21c05c57a39f

jest ReferenceError: Cannot access '' before initialization

I'm getting the error:
ReferenceError: Cannot access 'myMock' before initialization
Even though i respected jest documentation about the hoisting:
A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'.
I'm doing this:
import MyClass from './my_class';
import * as anotherClass from './another_class';
const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
jest.mock('./my_class', () => {
return {
default: {
staticMethod: jest.fn().mockReturnValue(
{
method1: mockMethod1,
method2: mockMethod2,
})
}
}
});
as you can see both of my variables respect the "standard" but are not hoisted properly.
Am I missing something ?
Obviously it works when I just pass jest.fn() instead of my variables, but i'm not sure how to be able to use these in my test later on.
None of the answers above solved my problem, so here's my solution:
var mockMyMethod: jest.Mock;
jest.mock('some-package', () => ({
myMethod: mockMyMethod
}));
Something about using const before the imports feels weird to me. The thing is: jest.mock is hoisted. To be able to use a variable before it you need to use var, because it is hoisted as well. It doesn't work with let and const because they aren't.
The accepted answer does not handle when you need to spy on the const declaration, as it is defined inside the module factory scope.
For me, the module factory needs to be above any import statement that eventually imports the thing you want to mock.
Here is a code snippet using a nestjs with prisma library.
// app.e2e.spec.ts
import { Test, TestingModule } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import * as request from 'supertest';
import mockPrismaClient from './utils/mockPrismaClient'; // you can assert, spy, etc. on this object in your test suites.
// must define this above the `AppModule` import, otherwise the ReferenceError is raised.
jest.mock('#prisma/client', () => {
return {
PrismaClient: jest.fn().mockImplementation(() => mockPrismaClient),
};
});
import { AppModule } from './../src/app.module'; // somwhere here, the prisma is imported
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
)};
To clarify what Jason Limantoro said, move the const above where the module is imported:
const mockMethod1 = jest.fn(); // Defined here before import.
const mockMethod2 = jest.fn();
import MyClass from './my_class'; // Imported here.
import * as anotherClass from './another_class';
jest.mock('./my_class', () => {
return {
default: {
staticMethod: jest.fn().mockReturnValue(
{
method1: mockMethod1,
method2: mockMethod2,
})
}
}
});
The problem that the documentation addresses is that jest.mock is hoisted but const declaration is not. This results in factory function being evaluated at the time when mocked module is imported and a variable being in temporal dead zone.
If it's necessary to access nested mocked functions, they need to be exposed as a part of export object:
jest.mock('./my_class', () => {
const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
return {
__esModule: true,
mockMethod1,
mockMethod2,
default: {
...
This also applies to manual mocks in __mocks__ where variables are accessible inside a mock only.
You should move your mocking above your imports; that could be the source of your issue. Imports are also hoisted, so multiple hoisted entries would be hoisted in order.
jest.mock('./my_class', () => {
const mockMethod = jest.fn()
const default = { staticMethod: jest.fn().mockReturnValue({ method: mockMethod }) };
return { default, mockMethod };
});
import MyClass, { mockMethod } from './my_class'; // will import your mock
import * as anotherClass from './another_class';
However, if you for some reason can't do that, you could use doMock to avoid hoisting behaviour. If this happens on the top of your file, it should be a 1 to 1 change.
const mockMyMethod = jest.fn();
jest.doMock('some-package', () => ({ myMethod: mockMyMethod }));
This solution works for me and it's pretty easy for vuejs+ jest.
Two points to note:
you should declare the absolute path and not '#/js/network/repositories'
the getter helps to defer the instantiation
const mockGetNextStatuses = jest.fn();
const mockUpdatePrintingStatus = jest.fn();
jest.mock('../../../../../../src/js/network/repositories/index.js', () => {
return {
get printing() {
return {
getNextStatuses: mockGetNextStatuses,
updatePrintingStatus: mockUpdatePrintingStatus,
}
}
}
});
or
jest.mock('../../../../../../src/js/network/repositories/index.js', () => ({
printing: {
getNextStatuses: jest.fn(),
updatePrintingStatus: jest.fn()
}
}));
import { printing } from '../../../../../../src/js/network/repositories/index.js';
// and mock the module
printing.getNextStatuses.mockReturnValue(['XX','YY']);
Example of using TypeScript with Jest and mockDebug.js module
jest.mock('debug', () => {
global.mockDebug = jest.fn();
return () => global.mockDebug;
});
// usage
describe('xxx', () => {
test('xxx', () => {
expect(global.mockDebug.mock.calls.toString()).toContain('ccc');
})
});

How to change mock implementation on a per single test basis?

I'd like to change the implementation of a mocked dependency on a per single test basis by extending the default mock's behaviour and reverting it back to the original implementation when the next test executes.
More briefly, this is what I'm trying to achieve:
Mock dependency
Change/extend mock implementation in a single test
Revert back to original mock when next test executes
I'm currently using Jest v21. Here is what a typical test would look like:
// __mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
// __tests__/myTest.js
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
Here is what I've tried so far:
mockFn.mockImplementationOnce(fn)
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
Pros
Reverts back to original implementation after first call
Cons
It breaks if the test calls b multiple times
It doesn't revert to original implementation until b is not called (leaking out in the next test)
jest.doMock(moduleName, factory, options)
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
Pros
Explicitly re-mocks on every test
Cons
Cannot define default mock implementation for all tests
Cannot extend default implementation forcing to re-declare each mocked method
Manual mocking with setter methods (as explained here)
// __mocks__/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
// __tests__/myTest.js
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
Pros
Full control over mocked results
Cons
Lot of boilerplate code
Hard to maintain on long term
jest.spyOn(object, methodName)
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
Cons
I can't revert mockImplementation back to the original mocked return value, therefore affecting the next tests
Use mockFn.mockImplementation(fn).
import { funcToMock } from './somewhere';
jest.mock('./somewhere');
beforeEach(() => {
funcToMock.mockImplementation(() => { /* default implementation */ });
// (funcToMock as jest.Mock)... in TS
});
test('case that needs a different implementation of funcToMock', () => {
funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
// (funcToMock as jest.Mock)... in TS
// ...
});
A nice pattern for writing tests is to create a setup factory function that returns the data you need for testing the current module.
Below is some sample code following your second example although allows the provision of default and override values in a reusable way.
const spyReturns = returnValue => jest.fn(() => returnValue);
describe("scenario", () => {
beforeEach(() => {
jest.resetModules();
});
const setup = (mockOverrides) => {
const mockedFunctions = {
a: spyReturns(true),
b: spyReturns(true),
...mockOverrides
}
jest.doMock('../myModule', () => mockedFunctions)
return {
mockedModule: require('../myModule')
}
}
it("should return true for module a", () => {
const { mockedModule } = setup();
expect(mockedModule.a()).toEqual(true)
});
it("should return override for module a", () => {
const EXPECTED_VALUE = "override"
const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
});
});
It's important to say that you must reset modules that have been cached using jest.resetModules(). This can be done in beforeEach or a similar teardown function.
See jest object documentation for more info: https://jestjs.io/docs/jest-object.
Little late to the party, but if someone else is having issues with this.
We use TypeScript, ES6 and babel for react-native development.
We usually mock external NPM modules in the root __mocks__ directory.
I wanted to override a specific function of a module in the Auth class of aws-amplify for a specific test.
import { Auth } from 'aws-amplify';
import GetJwtToken from './GetJwtToken';
...
it('When idToken should return "123"', async () => {
const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
getIdToken: () => ({
getJwtToken: () => '123',
}),
}));
const result = await GetJwtToken();
expect(result).toBe('123');
spy.mockRestore();
});
Gist:
https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2
Tutorial:
https://medium.com/p/b4ac52a005d#19c5
When mocking a single method (when it's required to leave the rest of a class/module implementation intact) I discovered the following approach to be helpful to reset any implementation tweaks from individual tests.
I found this approach to be the concisest one, with no need to jest.mock something at the beginning of the file etc. You need just the code you see below to mock MyClass.methodName. Another advantage is that by default spyOn keeps the original method implementation but also saves all the stats (# of calls, arguments, results etc.) to test against, and keeping the default implementation is a must in some cases. So you have the flexibility to keep the default implementation or to change it with a simple addition of .mockImplementation as mentioned in the code below.
The code is in Typescript with comments highlighting the difference for JS (the difference is in one line, to be precise). Tested with Jest 26.6.
describe('test set', () => {
let mockedFn: jest.SpyInstance<void>; // void is the return value of the mocked function, change as necessary
// For plain JS use just: let mockedFn;
beforeEach(() => {
mockedFn = jest.spyOn(MyClass.prototype, 'methodName');
// Use the following instead if you need not to just spy but also to replace the default method implementation:
// mockedFn = jest.spyOn(MyClass.prototype, 'methodName').mockImplementation(() => {/*custom implementation*/});
});
afterEach(() => {
// Reset to the original method implementation (non-mocked) and clear all the mock data
mockedFn.mockRestore();
});
it('does first thing', () => {
/* Test with the default mock implementation */
});
it('does second thing', () => {
mockedFn.mockImplementation(() => {/*custom implementation just for this test*/});
/* Test utilising this custom mock implementation. It is reset after the test. */
});
it('does third thing', () => {
/* Another test with the default mock implementation */
});
});
I did not manage to define the mock inside the test itself so I discover that I could mock several results for the same service mock like this :
jest.mock("#/services/ApiService", () => {
return {
apiService: {
get: jest.fn()
.mockResolvedValueOnce({response: {value:"Value", label:"Test"}})
.mockResolvedValueOnce(null),
}
};
});
I hope it'll help someone :)
It's a very cool way I've discovered on this blog https://mikeborozdin.com/post/changing-jest-mocks-between-tests/
import { sayHello } from './say-hello';
import * as config from './config';
jest.mock('./config', () => ({
__esModule: true,
CAPITALIZE: null
}));
describe('say-hello', () => {
test('Capitalizes name if config requires that', () => {
config.CAPITALIZE = true;
expect(sayHello('john')).toBe('Hi, John');
});
test('does not capitalize name if config does not require that', () => {
config.CAPITALIZE = false;
expect(sayHello('john')).toBe('Hi, john');
});
});

Categories

Resources