How to mock a called function using jest? [duplicate] - javascript

This question already has answers here:
How to mock functions in the same module using Jest?
(10 answers)
Closed last month.
I am writing tests for functions in a file. Several functions call another function which is also in that file. Here is how it looks:
function importantFunc(str) {
//some logic
return result;
}
function funcA() {
//some logic
const result = importantFunc('test A')
//some logic
}
function funcB() {
//some logic
const result = importantFunc('test B')
//some logic
}
I have already written a test for importantFunc. Problem is how can I test mock the call to importantFunc in funcA and funcB so that they will not call importantFunc directly, but use the result I provide in my mock?
Between jest.spyOn and jest.fn, I am thinking jest.fn may work. But I cannot figure out how it will tie to funcA or funcB.

You generally can't, unless if you're importing importantFunc from another module, in which case you mock the whole module.
What you should do is refactor your code so your units are not tightly coupled to one-another.
// in impl
function funcA(importantFunc) {
//some logic
const result = importantFunc('test A')
//some logic
}
function funcB(importantFunc) {
//some logic
const result = importantFunc('test B')
//some logic
}
// ...
// ...
// in test
const importantFunc = jest.fn(() => { ... });
funcB(importantFunc)

Related

How to make Bootstrapper object of window be available for unit testing?

I have a function in my project which calls a function from bootstrapper object of window. Below is the function:
export default function measurement(analObj) {
if (window.Bootsrapper._trackAnalytics === function) {
window.Bootstrapper._trackAnalytics(analObj);
}
}
I wrote below code to unit test this function in jest:
import measurement from "../helpers/measurement";
describe('Test measurement', () => {
beforeAll(() => {
const Bootstrapper = {
_trackAnalytics: function(obj) {
return obj;
},
};
window.Bootstrapper = Bootstrapper;
})
test('should send analytics object to rtrack analyitics', () => {
const testObj = {
pageName: "Leave Abasence"
}
const result = measurement(testObj);
expect(testObj).toEqual(result);
})
})
I get "undefined" for result variable that comes from measurement function call as I am unable to make window.measurement._trackAnalytics function available for measurement function at run time.
I would like to know:
Is my approach correct to unit test this scenario? If Yes, How to make the _trackAnalytics function available for measurement function while unit test run time.
Please suggest any other better approach if you know.
The window.measurement._trackAnalytics function is indeed available for measurement function when your test runs. Otherwise, you would get a TypeError for calling something that is not a function.
The problem is that in the measurement method there is nothing being returned. The _trackAnalytics method is called but its result is not returned. That's why you get undefined as a result.
In order to check that it is indeed being called I would use a jest mock function. The test would look like:
test('should send analytics object to rtrack analyitics', () => {
const testObj = {
pageName: 'Leave Abasence'
};
measurement(testObj);
expect(window.Bootstrapper._trackAnalytics).toHaveBeenCalledTimes(1);
expect(window.Bootstrapper._trackAnalytics).toHaveBeenCalledWith(testObj);
});
Note that your code has some problems (which I expect are typos). In the if condition you are checking Bootsrapper instead of Bootstrapper. And you are checking if it is equal to function instead of checking with typeof. I think the line should look:
if (typeof window.Bootstrapper._trackAnalytics === 'function') {

How do I mock timer using jest?

How do use Jest to mock timer context.getRemaining() and mock sendMessage() when it has been called?
I want context.getRemaining() to be decreased by 9sec when sendMessage()has been called. sendMessage() will be called multiple time via while loop. It will start with 60,000ms.
exports.handler = async (context) => {
while (true) {
console.log(context.getRemaining());
await sendMessage();
if (context.getRemaining() < 13000) {
break;
}
}
return "FINISH";
}
You can use jest.fn() for context.getRemaining and provide multiple functions for it:
sendMessage = jest.fn(async () => {});
await handler({
getRemaining: jest.fn()
.mockReturnValueOnce(20000)
.mockReturnValueOnce(10000),
});
expect(sendMessage).toHaveBeenCalledTimes(2)
When you write tests, usually it's better to avoid complex logic in the test code. For the mocks you can provide explicit return values. Else you'll need to write tests for your test code, which is bad.

Angular JS Unit testing: how to test function in service that doesn't return

I'm sorry for possible duplicate, I didn't find any solution for my problem.
I'm need to write tests for a service in Angular JS app.
So I have one main function that returns and uses as an external method. And a couple help functions for external. So how can I call and test things inside help functions (subFunc)?
service.js
function TestService() {
return {
mainFunc: mainFunc
};
funcion mainFunc() {
//do some and call subFunc()
subFunc(a)
}
function subFunc(a) {
if (a === 1) {
// ... magic 1
return true;
} else {
// ... magic 2
return false;
}
}
}
})()
service.spec.js
describe('Test Service', function() {
beforeEach(module('TestService'));
var TestService;
beforeEach(inject(function($injector) {
TestService = $injector.get('TestService');
}));
it('should return true if subFunc called with 1', function () {
// ....
});
})
Rewrite TestService to expose subFunc so that you can stub it with a spy and track whether it was called.
That is one of the reasons you write tests before and while writing the code: they will influence the style of the code so as to make it easier to test. In this case subFunc has to be externally visible to allow easy testing so make it visible.

node: mocking a function with a callback argument

I am trying to write unit tests for a function that reads a jsonfile into an object. I read the file with
jsonfile.readFile(filename, function (err, obj) {
//...
});
For my unit tests, I want to mock this function so that, rather than actually reading the file, it will simply return a fixed json block and pass it into the callback.
What I'm having trouble with is how to mock the function. I've seen sinon, which says it supports mocking functions, but I can't find anything that describes how to actually define custom behavior for the function I'm mocking. Sinon looks like it allows me to define what I want the function to return, how often I expect it to be called, etc, but not actually define a mocked function.
Basically, I want something like this:
mock(jsonfile, 'readFile', function(filename, callback) {
callback(null, {attr1:"foo"});
});
How do I do this with sinon?
But actually, why don't you just replace readFile by a function with the same definition (so that it doesn't break the code using it). And just return your mock data.
jsonfile.readFile = function(filePath, callback) {
callback(null, { mockData: "foo" });
};
easy as that.
Otherwise, you can use a Proxy if you don't want to deal with the definition :
const jsonfile = {
readFile: function(filename, callback) {
callback();
}
};
// intercept every call to readFile and always return the mock data
jsonfile.readFile = new Proxy(jsonfile.readFile, {
apply: function(target, thisArg, args) {
return args[1](null, { someMocking: "" });
}
});
// call readFile as usual
jsonfile.readFile('testfile', function(err, result) {
console.log(result);
});
Proxies work as interceptors.
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy
This is not straightforward in testing because it involved callbacks. You need to test wether a callback you passed to readFile was called with right arguments, which in this case is the dummyFile.
import sinon from 'sinon'
import jsonfile from './jsonfile'
const jsonFileMock = sinon.spy(jsonfile, 'readFile');
const callback = sinon.spy();
jsonfile.readFile(callback);
jsonFileMock.callsArgWith(1, 'dummyFileName');
expect(callback.calledWith('dummyFileName')).to.be.true;
jsonFileMock.restore();
If you want to abstract this into a function, than it can be something like :
function mock(module, method, ...callbacks){
const stub = sinon.stub(jsonfile, 'readFile');
callbacks.forEach((callback, index) => {
stub.callsArgWith(index, callback);
});
}
The function I was looking for is stub.callsFake():
> Thing = {
... meth : function() { console.log(1) }
... }
> Thing.meth()
1
> var stub = sinon.stub(Thing, 'meth')
> stub.callsFake(function() { console.log(2) })
> Thing.meth()
2
> stub.restore()
> Thing.meth()
1
It doesn't look like mock is capable of what I want to do.

jasmine: Is this the only difference between returnValue and callFake?

The only difference between callFake and returnValue is that callFake can return different values on the basis of custom logic (parameters/environment)?
Are there any other differences?
callFake(() => {...}) takes a call back function
If we just want a return value when a service method is called then we can use any of and.callFake or and.returnValue
component file:
#Component(...)
export class DependencyComponent {
constructor(private service: RandomService){....}
.....
sampleMethod() {
return this.service.randomMethod();
}
.....
}
unit test case for above component:
it('test callFake vs returnValue', () => {
let randomService= new RandomService();
let component = new DependencyComponent(randomService);
spyOn(randomService, 'randomMethod').and.callFake(() => 4)
expect(component.sampleMethod()).toBe(4)
spyOn(randomService, 'randomMethod').and.returnValue(10);
expect(component.sampleMethod()).toBe(10)
})
in above case both the ways are correct.
Suppose we are passing a parameter to service method to perform its logic then in that case we have to use and.callFake((param) => {...}). Here param parameter will be the argument passed to the spied method.
component file:
#Component(...)
export class DependencyComponent {
constructor(private service: RandomService){....}
.....
sampleMethod(val) {
return this.service.randomMethod(val);
}
.....
}
unit test case for above component:
it('test callFake vs returnValue', () => {
let randomService= new RandomService();
let component = new DependencyComponent(randomService);
spyOn(randomService, 'randomMethod').and.callFake((param) => param+4)
expect(component.sampleMethod(4)).toBe(8);
expect(component.sampleMethod(12)).toBe(16)
})
when component.sampleMethod(4) is executed it will call this.service.randomMethod(4). As randomMethod() is being spied using and.callFake therefore 4 will be passed as argument of call back function of and.callFake.
Clear difference or benefit can be seen when callfake has callback function as parameter .
I have observed a strange behavior and differece between returnValue and callFake in my Angular 8 code (with jasmine). Below is my code,
spyOn(object, 'method').and.returnValue(Promise.reject('error'));
When I have the above code, when I call tick() the above spy method gets invoked on its own - without calling the component method. That results in my test failing with error Uncaught promise error. However, when I change the above code to this,
spyOn(object, 'method').and.callFake(() => Promise.reject('error'));
the code work as expected and the spy method is called only when invoked from within the component.
Unable to understand what is the difference here and why callFake works.
Both are same.. returnValue is just syntactical sugar over callfake is what I understand.

Categories

Resources