Jest mocking inside the same module - javascript

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);
});
});

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);
});

Several mocks of one module in one file with jest

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.

How To Reset Manual Mocks In Jest

I have a manual mock of crypto that looks like this:
// __mocks__/crypto.js
const crypto = jest.genMockFromModule('crypto')
const toString: Function = jest.fn(() => {
return {}.toString()
})
const mockStringable = {toString}
const update: Function = jest.fn(() => mockStringable)
const deciper = {update}
crypto.createDecipheriv = jest.fn(() => deciper)
export default crypto
Which is basically tested like this:
const crypto = require('crypto')
jest.mock('crypto')
describe('cookie-parser', () => {
afterEach(() => {
jest.resetAllMocks()
})
describe('decryptCookieValue', () => {
it('should call the crypto library correctly', () => {
const result = decryptCookieValue('test-encryption-key', 'test-encrypted-value')
expect(crypto.pbkdf2Sync).toHaveBeenCalledTimes(2)
expect(crypto.createDecipheriv).toHaveBeenCalled()
// more tests, etc, etc, etc
expect(crypto.createDecipheriv('', '', '').update).toHaveBeenCalled()
expect(result).toEqual({}.toString())
})
})
...
This works however if in that same test file, I test another method that invokes decryptCookieValue from within crypto.createDecipheriv no longer returns my mock decipher. Instead it returns undefined. For instance:
describe('cookie-parser', () => {
afterEach(() => {
jest.resetAllMocks()
})
describe('decryptCookieValue', () => {
it('should call the crypto library correctly', () => {
const result = decryptCookieValue('test-encryption-key', 'test-encrypted-value')
expect(crypto.pbkdf2Sync).toHaveBeenCalledTimes(2)
expect(crypto.createDecipheriv).toHaveBeenCalled()
expect(crypto.createDecipheriv('', '', '').update).toHaveBeenCalled()
expect(result).toEqual({}.toString())
})
})
...
...
describe('parseAuthenticationCookie', () => {
it('should create the correct object', () => {
// parseAuthenticationCookie calls decryptCookieValue internally
const result = parseAuthenticationCookie('', '') // Fails because internal call to crypto.createDecipheriv stops returning mock decipher.
expect(result).toEqual({accessToken: null})
})
})
})
I think this is an issue with resetting the manual mock because if I take that later test and move it into a file all by itself with the same surrounding test harness it works just fine.
// new test file
import crypto from 'crypto'
import { parseAuthenticationCookie } from './index'
jest.mock('crypto')
describe('cookie-parser', () => {
afterEach(() => {
jest.resetAllMocks()
})
describe('parseAuthenticationCookie', () => {
it('should create the correct object', () => {
// Works just fine now
const result = parseAuthenticationCookie('', '')
expect(result).toEqual({accessToken: null})
})
})
})
Is my assessment here correct and, if so, how do I reset the state of the manual mock after each test?
From Jest docs:
Does everything that mockFn.mockClear() does, and also removes any mocked return values or implementations.
ref: https://jestjs.io/docs/en/mock-function-api#mockfnmockreset
In your example you are assuming that calling resetAllMocks will set your manual mock back and it's not.
The reason why your test works in a separate file is because jest runs each file isolated, which is nice since you can screw up only the specs living in the same file.
In your particular case something that might work is calling jest.clearAllMocks() (since this will keep the implementation and returned values).
clearMocks options is also available at the jest config object (false as default), if you want to clear all your mocks on every test, this might be handy.
Hope this help you or anyone else having having a similar issue.
Bonus tip (no quite related) If you are mocking a module that it's being used internally by other module and in some specific test you want to mock that module again with a different mock, make sure to require the module that it's using the mocked module internally again in that specific test, otherwise that module will still reference the mock you specified next to the imports statements.
Looks like the better way to test this is something on the lines of:
jest.mock('crypto')
describe('decrypt()', () => {
afterEach(() => {
jest.resetAllMocks()
})
it('returns value', () => {
const crypto = require('crypto')
const encryptedValue = 'encrypted-value'
const update = jest.fn()
const pbkdf2SyncResult = 'test result'
crypto.pbkdf2Sync = jest.fn().mockImplementation(() => {
return pbkdf2SyncResult
})
crypto.createDecipheriv = jest.fn().mockImplementation((format, key, iv) => {
expect(format).toEqual('aes-256-cbc')
expect(key).toEqual(pbkdf2SyncResult)
expect(iv).toEqual(pbkdf2SyncResult)
return {update}
})
decrypt(encryptedValue)
const inputBuffer = Buffer.from(encryptedValue, 'base64')
expect(update).toHaveBeenCalledWith(inputBuffer)
})
})
This way I don't even have to have the manual mock and I can use mockImplementationOnce if I need to have the mock reset.

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');
});
});

how can I spy on a function with jest

I have a function that calls another function
const getArtist = (id) => {
// builds params for other function
return otherFuntion(params);
}
I want to verify that when I call getArtist(someValue), otherFunction gets called with the right arguments.
How can I accomplish that with jest ?
describe('getArtist function', () => {
it('should call otherFunction with the right params', () => {
// how can I get a handle on the mock of otherFunction to see
// if it was called correctly ?
});
});
You could use proxyquire to mock out the imported function at compile time if it's a public function. If it's a private function then you definitely don't want to mock it out.
You should probably just test the return value of otherFuntion instead.
So you are really calling the function otherFuntion(params) so I would rather check if return of your getArtist functions gives the same results as otherFunction called with right params.
describe('getArtist function', () => {
it('should call otherFunction with the right params', () => {
expect(getArtist(id)).toBe(otherFunction(params));
});
});
Or create special function for generate params and test it separatly. Like prepareArtistParams and use it inside:
const getArtist = (id) => {
const params = prepareArtistParams(id);
return otherFuntion(params);
}
describe('otherFuntion function', () => {
it('should create right params', () => {
expect(prepareArtistParams(id)).toBe(params);
});
});

Categories

Resources