Mock.mockImplementation() not working - jestjs

I have a service class
Service.js
class Service {
}
export default new Service();
And I am trying to provide a mock implementation for this. If I use something like this:
jest.mock('./Service', () => { ... my mock stuff });
It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.
I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)
import service from './Service';
jest.mock('./Service', () => jest.fn);
service.mockImplementation(() => {
return { ... mock stuff }
);
Unfortunately when I am trying to run this, I get the below error:
TypeError: _Service2.default.mockImplementation is not a function

I had same problem as #Janos, the other answers didn't help either. You could do two things :
If you need to mock only a function from Service, in your test file:
import service from './Service';
jest.mock('./Service', () => jest.fn());
service.yourFunction = jest.fn(() => { /*your mock*/ })
 
If you need to mock the entire Module:
Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:
const myObj = {foo: "bar"}
const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
const myFunction2 = ...
module.exports = {
myFunction1,
myFunction2
}
then in your test file you just add:
jest.mock('./javascript/utils/service')
...functions exported from the mockfile will be then hit through your test file execution.

The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.
So this:
jest.mock('./Service', () => jest.fn);
Should be:
jest.mock('./Service', () => jest.fn());

ran into similar issues and resolved it by using .mockImplementationOnce
jest.mock('./Service', () => jest.fn()
.mockImplementationOnce(() => {
return { ... mock stuff }
})
.mockImplementationOnce(() => {
return { ... mock other stuff }
})
);
now when you run another test it will return the second mock object.

You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.
// Service.js
class Service {
}
export default new Service();
// main.test.js (main.js contains "import Service from './Service';")
const mockService = () => jest.fn();
jest.mock('./Service', () => {
return {
default: mockService
}
});

I had similar problem, and the cause was that ".spec.js" file had an
import jest from "jest-mock";
After removing this line, it worked.

My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.
For example, change this:
let value;
let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
value = changes["testValue"];
});
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
});
to this:
let value;
let onPropertyChange: OnPropertyChangeCallback;
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
onPropertyChange = jest.fn((changes: any) => {
value = changes["testValue"];
});
});

Related

How can I mock a function called by the function I am exercising? The functions are in the same file [duplicate]

I have one file called helper.js that consist of two functions
export const funcA = (key) => {
return funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
I have my helper.spec.js to test the helper.js file functions.
import {funcA,funcB} from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
}
}
The test for funcB is pretty simple i just call it and expect someObj
The problem is to test funcA, in order to test it i want to mock the response of funcB.
I want testFuncB call the actual funcB and testFuncA call mocked funcB
How can i achieve funcB to be mocked and original in my two tests?
This is not a duplicate. It is a different case: they mock inner called functions only, if I remove the testFuncB then it will be the same but I must perform test on testFuncB too.
If an ES6 module directly exports two functions (not within a class, object, etc., just directly exports the functions like in the question) and one directly calls the other, then that call cannot be mocked.
In this case, funcB cannot be mocked within funcA the way the code is currently written.
A mock replaces the module export for funcB, but funcA doesn't call the module export for funcB, it just calls funcB directly.
Mocking funcB within funcA requires that funcA call the module export for funcB.
That can be done in one of two ways:
Move funcB to its own module
funcB.js
export const funcB = () => {
return 'original';
};
helper.js
import { funcB } from './funcB';
export const funcA = () => {
return funcB();
};
helper.spec.js
import * as funcBModule from './funcB';
import { funcA } from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(funcBModule.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(funcBModule, 'funcB');
spy.mockReturnValue('mocked');
expect(funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});
Import the module into itself
"ES6 modules support cyclic dependencies automatically" so it is perfectly valid to import a module into itself so that functions within the module can call the module export for other functions in the module:
helper.js
import * as helper from './helper';
export const funcA = () => {
return helper.funcB();
};
export const funcB = () => {
return 'original';
};
helper.spec.js
import * as helper from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(helper.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(helper, 'funcB');
spy.mockReturnValue('mocked');
expect(helper.funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});
Late answer but this should work.
Also you should test funcB in its own file and not inside the 'helper' tests.
import { funcB } from './funcB';
import { funcA } from './helper';
jest.mock('./funcB');
describe('helper', () => {
test('test funcA', () => {
const funcBSpy = jest.fn();
funcB.mockImplementation(() => funcBSpy());
funcA();
expect(funcBSpy).toHaveBeenCalledTimes(1);
});
});
import * as helper from 'helper';
describe('helper', () => {
it('should test testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
mockTestFuncB.spyOn(helper, 'testFuncB').mockReturnValue(/*your expected return value*/);
// test logic
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}
I create a kind of nameSpace to handle this issue:
let helper = {}
const funcA = (key) => {
return helper.funcB(key)
};
const funcB = (key,prop) => {
return someObj;
};
helper = { funcA, funcB }
module.exports = helper
and then mocking is obvious with jest.fn
You can use babel-plugin-rewire provided __set__ function to mock internal function.
Assuming you have set up babel-plugin-rewire.
helper.spec.js
import {funcA, __set__} as helper from './helper';
describe('helper', () => {
test('test funcA', () => {
__set__('funcB', () => {
return 'funcB return value'
})
expect(funcA()).toBe('funcB return value');
});
});
One advantage of this solution is you don't need to change any original code
I was able to get this working. I separated my helper and my main logic into two files like other solutions. In the test file, I had to mock the entire helper file.
const { doAdd } = require('./addHelper');
function add(a, b) {
return doAdd(a, b);
}
jest.mock('./addHelper');
// ...
it('should call doAdd', () => {
// hook into doAdd helper method and intercept its return value
jest.spyOn(helperModule, 'doAdd').mockReturnValue(11);
expect(addModule.add()).toEqual(11);
expect(helperModule.doAdd).toBeCalled();
});
Here is my solution:
https://github.com/davidholyko/jest-sandbox
You can do the following trick when you test the funcA:
1.Mock the funcB:
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
2.Change the funcB(key) to this.funcB(key)
I had the same problem and worked! Full Code:
export const funcA = (key) => {
return this.funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
Test Code:
import helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
...
}
test('testFuncA', () => {
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
}
}
I think this might work
import * as helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
jest.spyOn(helper, 'testFuncB').mockImplementationOnce(mockTestFuncB);
// Do the testing ...
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}

Jest mock values returned by function

I have a logger file as below which implements logging functionality. uuidLogger.js
const winston = require('winston'),
CustomTransport = require('./customTransport');
function getLogger(route) {
return winston.createLogger({
defaultMeta: { route },
transports: [new CustomTransport()]
});
}
module.exports = getLogger;
It is imported by a function like this and used for logging testfn.js
const uuidLogger = require('./uuidLogger')('test-fn');
function testMock() {
uuidLogger.info('Hey I am just logging');
}
module.exports = { testMock };
I am trying to mock uuidlogger in testfn.js so that I can track various methods called on uuidLogger object. I tried below approach.
import { testMock } from './testfn';
import getLogger from './uuidLogger';
const logger = getLogger('testfn');
jest.mock('./uuidLogger', () =>
jest.fn(() => ({
info: jest.fn(() => console.log('Mocked function actually called'))
}))
);
it('verify that info method was called on returned object', () => {
testMock('abx');
expect(logger.info).toBeCalledTimes(1);
});
It was able to mock the method called however mock information is not getting reflected in logger.info object.
I also tried below approach
import { testMock } from './testfn';
import getLogger from './uuidLogger';
jest.mock('./uuidLogger', () =>
jest.fn(() => ({ info: jest.fn(() => console.log('Top level fn')) }))
);
const logger = {
error: jest.fn(),
info: jest.fn(() => {
console.log('Overwritten fn');
})
};
getLogger.mockReturnValue(logger);
it('shud return Winston instance', () => {
testMock('abx');
expect(logger.info).toBeCalledTimes(1);
});
Any help on how to get it will be appreciated. Thanks in advance.
It seems to be the assertion is not done on proper variable.
Need to assert on getLogger
Your first approach of writing test case is proper.
Add assertion something like this:
expect(getLogger.mock.results[0].value.info).toBeCalledTimes(1);

mock a toPromise function in jest - got .toPromise is not a function

I have an httpService from nestjs/common
and I am using like the following:
const response = await this.httpService.post(`${this.api}/${action}`, data).toPromise();
and in my jest spec file ( unit testing) . i am trying to mock this service
httpServiceMock = {
post: jest.fn()
};
it('should start', async () => {
const serviceResult = await service.start(data);
});
and I have got this error :
TypeError: this.httpService.post(...).toPromise is not a function
I am also trying to add a promise result like :
const promise = Promise.resolve('result');
httpServiceMock.post.mockResolvedValue(promise);
tried also :
it('should start', async () => {
const mockObservable = Promise.resolve({
toPromise: () => {
console.log('toPromise called');
}
})
httpServiceMock.post.mockImplementation(() => mockObservable);
const serviceResult = await service.start();
});
My question is how can I mock the promise and return a response or exception
The return value httpService.post needs to return an Observable<AxiosResponse<T>> which includes a property toPromise, which is a function. Your mock returns a resolved promise, whereas it needs to return a fake Observable.
The Observable is returned immediately, so the post implementation can just return a raw value, but the toPromise needs to return a promise.
Return the correct shaped object to get rid of this error:
const mockObservable = {
toPromise: () => Promise.resolve('result')
}
httpServiceMock.post.mockImplementation(() => mockObservable);
I had a similar problem that could not be solved by accepted answer. So I bring here another solution just in case it could help someone else.
If you have jasmine, just use jasmine.createSpyObj(). If not, here is what I needed to do :
First, I implemented a jasmine.createSpyObj() equivalent (based on this answer with little modifications) :
export class TestUtilsService {
static createSpyObj (baseName:string, methodNames:string[]): SpyObject {
let obj: any = {};
for (let i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = jest.fn();
}
return {[baseName]:()=>obj};
};
}
export class SpyObject {
[key: string]: ()=>{[key:string]:jest.Mock} ;
}
Then I used it in my unit test :
const spyHttpClient: SpyObject = TestUtilsService.createSpyObj('get',['toPromise']);
Add it in test module providers :
{provide: HttpClient, useValue: spyHttpClient}
Finally, mock the toPromise implementation in order to return a mocked response :
const mockedResponse = {...};
spyHttpClient.get().toPromise.mockImplementationOnce(()=>Promise.resolve(mockedResponse));
await service.myRealMethodThatCallsHttpClient();
expect(service.someUpdatedProp).toBeTruthy();
Please notice parenthesis after method get.
"A Jar of Clays" solution didn't work for me (I got mockImplementation is not a function), but this worked:
const mockPromise = {
toPromise: () => Promise.resolve(ical)
}
mockHttpService.get = jest.fn( () => {return mockPromise});

Make partial mock of class with Jest in NodeJS

I'm new to NodeJS and came from PHP, where creating partial mock was easy. But I'm not able to accomplish the same with Jest in NodeJS.
I have a function extractPayloadDates which accept an instance of dialogflow Agent and taking and parsing data from it. I want to mock only single method getParameter of Agent, because no more methods are used in the tested function. I found this code online but it doesn't work
import { Agent } from '../../src/dialogflow/Agent';
import { extractPayloadDates } from '../../src/intents/extractPayloadDates';
describe('extractPayloadDates', () => {
it('tests extracting string', () => {
const AgentMock = jest.fn<Agent, []>(() => ({
getParameter: () => {
return 'some date';
}
}));
const agent = new AgentMock();
expect(extractPayloadDates(agent)).toEqual('some date');
});
});
This code produce following error:
Type '{ getParameter: () => string; }' is missing the following properties from type 'Agent': payload, webhookClient, chatter, getOriginalRequest, and 13 more.ts(2740)
index.d.ts(124, 53): The expected type comes from the return type of this signature.
I also tried to use jest.spyOn, but the problem is, that I cannot create Agent instance as it needs many other objects.
Edit 3.9.2019 with more code
Agent.ts
export class Agent {
private payload: DialogFlowPayload[] = [];
constructor(readonly webhookClient: WebhookClient, private readonly chatter: Chatter) {}
...
}
WebhookClient and Chatter have more dependencies in their constructor as well...
extractPayloads.spec.ts
import { Agent } from '../../src/dialogflow/Agent';
import { extractPayloadDates } from '../../src/intents/extractPayloadDates';
describe('extractPayloadDates', () => {
it('tests extracting string', () => {
const webhookMock = jest.fn();
const chatter = jest.fn();
const agent = new Agent(webhookMock, chatter);
expect(extractPayloadDates(agent)).toEqual('some date');
});
});
This produce another error:
Argument of type 'Mock' is not assignable to parameter of type 'Chatter'.
Property 'getMessage' is missing in type 'Mock' but required in type 'Chatter'
Do I really have to create also WebhookClient and all its dependencies and do the same with Chatter? If I do, I have to create instances of multiple classes just to mock 1 method in Agent, which then will not use any of dependency.
From the docs of jest
If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or object[methodName] = jest.fn(() => customImplementation);
jest.spyOn call jest.fn internally. So you can only mock getParameter method of agent like this:
extractPayloadDates.ts:
function extractPayloadDates(agent) {
return agent.getParameter();
}
export { extractPayloadDates };
Agent.ts:
interface DialogFlowPayload {}
interface WebhookClient {}
interface Chatter {
getMessage(): any;
}
class Agent {
private payload: DialogFlowPayload[] = [];
constructor(readonly webhookClient: WebhookClient, private readonly chatter: Chatter) {}
public getParameter() {
return 'real data';
}
public otherMethod() {
return 'other real data';
}
}
export { Agent, Chatter };
Unit test, only mock getParameter method of agent, keep the original implementation of otherMethod
import { extractPayloadDates } from './extractPayloadDates';
import { Agent, Chatter } from './Agent';
const webhookMock = jest.fn();
const chatter: jest.Mocked<Chatter> = {
getMessage: jest.fn()
};
const agent = new Agent(webhookMock, chatter);
describe('extractPayloadDates', () => {
it('should only mock getParameter method of agent', () => {
agent.getParameter = jest.fn().mockReturnValueOnce('mocked data');
const actualValue = extractPayloadDates(agent);
expect(actualValue).toEqual('mocked data');
expect(agent.otherMethod()).toBe('other real data');
});
});
PASS src/stackoverflow/57428542/extractPayloadDates.spec.ts
extractPayloadDates
✓ should only mock getParameter method of agent (7ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.484s, estimated 3s

Is it possible to mock local functions using Jest

I have a file (file.js) consisting of 3 functions in the same file:
file.js:
const fn1 = (number) => {
return number + 1;
}
export const fn2 = (value) => {
return value;
}
export const fn3 = (number) => {
return fn1(number);
}
export const fn4 = (number) => {
return fn2(number);
}
Is it possible to mock out these functions periodically using JEST? What I hope to achieve is something along these lines:
file.test.js:
import { fn2, fn3, fn4 } from 'file.js';
describe('fn2', () => {
test('normal test.' => {
expect(fn2(1)).toBe(1);
})
});
describe('fn3', () => {
test('mock fn1 for this test.' => {
mock fn1 = () => { return 1 };
expect(fn3(2)).toBe(1);
})
});
describe('fn4', () => {
test('mock fn2 for this test.' => {
mock fn2 = () => { return 1 };
expect(fn4(2)).toBe(1);
})
});
It is not possible through the way how JS modules work. Only exported functions are visible to the outside, all other stuff is only visible in the scope of the module, so there is no way to mock it. Also there is no need to mock this function as you only want to test the public API.
The only way to test the function itself and mock it for this module is to put it into an own module, which makes sense if it is some really complicated function where you need good test coverage and it is to complicated to get this when its in another module.

Resources