How to spy a function in Jest that would run on set? - javascript

I have an object like this:
const obj = {
}
And I want to transform it and add a method, and that method will run if a prop exist in that object.
function transform(obj){
obj.test = () => {
//Do something
}
if(obj.target){
obj.test()
}
}
transform(obj)
How can I test if this function has run or not? If using jest.spyOn, I can only spy on it after this method has been added to the obj, which by then the method has already run.
EDIT
This is the actual code and the test I am struggling with:
https://github.com/winston0410/camouflage/blob/1811aa85e6f3f966636c3d28f26b91941456a03a/tests/hydration.test.js#L33-L45

Related

Why a test with function called from another function in the mocked class fail?

I have an issue with testing a pretty simple of javascript code using jest. So I post the code:
Foo.js
export class Foo {
constructor() {
}
someFn() {
this.internalFn();
}
internalFn() {
}
}
Foo.spec.js
jest.mock('../src/foo');
import {Foo} from '../src/foo';
describe('Foo', () => {
it ('test foo', () => {
const foo = new Foo();
foo.someFn();
expect(foo.someFn.mock.calls.length).toBe(1);
expect(foo.internalFn.mock.calls.length).toBe(1); // why received 0 ???
})
})
Why second expect fail? foo.internalFn is called from foo.someFn.
A mock function will actually not do anything (except for counting how often it was called, etc.)
That's why your internalFn() will bever be called, when you mock the function someFn().
// EDIT: Just some clarifications
When you unit test your software you want to test it modular and isolated.
Functions should be able to work on their own even when they're called internally by other functions.
When you want to test the function someFn() you have to mock internalFn(), because you want to check if internalFn() was actually called by calling someFn().
If you want to to test anything else that will call someFn() you have to mock that instead.

How mock a child function's output inside a parent that is being tested?

I'm wondering how I can mock the output of a child function inside a parent function during tests so the subsequent conditionals can be tested.
const someFunction = async () => {
const {x, y} = await functionThatFetchesObject();
// how to mock x,y values so following code can be tested?
if (x) {
// some code
} else if (y) {
// some code
} else {
// some code
}
}
Looks like you're trying to test the output of an API call, which is great! You could try extracting functionThatFetchesObject to a seperate file, and then testing that function specifically with Jest Mocks

Jest: how to count call from mock methods called via `call` or `apply`?

How can I use mocks to count function calls made via call or apply
// mylib.js
module.exports = {
requestInfo: function(model, id) {
return `The information for ${model} with ID ${id} is foobar`;
},
execute: function(name) {
return this[name] && this[name].apply(this, [].slice.call(arguments, 1));
},
};
// mylib.test.js
jest.mock('./mylib.js');
var myLib = require('./mylib.js');
test('', () => {
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // Success!
expect(myLib.requestInfo.mock.calls.length).toBe(1); // FAIL
});
If I explicitly call myLib.requestInfo, the second expectation succeeds.
Is there a way to watch module mock calls whose functions were called via apply or call?
From the jest.mock doc:
Mocks a module with an auto-mocked version when it is being required.
The docs could probably be improved with a better description of what "auto-mocked version" means, but what happens is that Jest keeps the API surface of the module the same while replacing the implementation with empty mock functions.
So in this case execute is getting called but it has been replaced by an empty mock function so requestInfo never gets called which causes the test to fail.
To keep the implementation of execute intact you will want to avoid auto-mocking the entire module and instead spy on the original function with something like jest.spyOn:
var myLib = require('./mylib.js');
test('', () => {
jest.spyOn(myLib, 'execute'); // spy on execute
jest.spyOn(myLib, 'requestInfo') // spy on requestInfo...
.mockImplementation(() => {}); // ...and optionally replace the implementation
myLib.execute('requestInfo', 'Ferrari', '14523');
expect(myLib.execute.mock.calls.length).toBe(1); // SUCCESS
expect(myLib.requestInfo.mock.calls.length).toBe(1); // SUCCESS
});

Sinon Spy to check function has been called

I am trying to use sinon.spy() to check that a function has been called. The function is called getMarketLabel and it returns marketLabel and accepts it into the function. I need to check that getMarketLabel has been called. I actually call getMarketLabel in one place, like so:
{getMarketLabel(sel.get('market'))}
The code I have so far is:
describe('Check if it has been called', () => {
let spy;
beforeEach(() => {
spy = sinon.spy(getMarketLabel, 'marketLabel');
})
it('should have been called', () => {
expect(spy).to.be.calledWith('marketLabel');
});
});
This is the error I receive:
TypeError: Attempted to wrap undefined property marketLabel as function
Sinon can't spy on functions that aren't a property of some object, because Sinon has to be able to replace the original function getMarketLabel by a spied-on version of that function.
A working example:
let obj = {
getMarketLabel(label) {
...
}
}
sinon.spy(obj, 'getMarketLabel');
// This would call the spy:
obj.getMarketLabel(...);
This syntax (which is close to what you're using) also exists:
let spy = sinon.spy(getMarketLabel);
However, this only triggers the spy code when explicitly calling spy(); when you call getMarketLabel() directly, the spy code isn't called at all.
Also, this won't work either:
let getMarketLabel = (...) => { ... }
let obj = { getMarketLabel }
sinon.spy(obj, 'getMarketLabel');
getMarketLabel(...);
Because you're still calling getMarketLabel directly.
This is the error I receive: TypeError: Attempted to wrap undefined
property marketLabel as function
You need to require the helper.js into your test file, then replace the relevant method on the required module and finally call the method replaced with the spy:
var myModule = require('helpers'); // make sure to specify the right path to the file
describe('HistorySelection component', () => {
let spy;
beforeEach(() => {
spy = sinon.stub(myModule, 'getMarketLabel'); // replaces method on myModule with spy
})
it('blah', () => {
myModule.getMarketLabel('input');
expect(spy).to.be.calledWith('input');
});
});
You cannot test whether the spy is called with helpers.sel('marketLabel') as this function will be executed before the test is conducted. You will therefore by writing:
expect(spy).to.be.calledWith(helpers.sel('marketLabel'));
be testing that that the spy is called with whatever value returned by helpers.sel('marketLabel') (which is undefined by default).
The content of helper.js should be:
module.exports = {
getMarketLabel: function (marketLabel) {
return marketLabel
}
}

Writing JavaScript tests that test other functions are called, without actually calling them

I have been tasked with writing unit tests for some AngularJS code that was written by another team, who didn't write any tests
They have written the following function but I cannot figure out how to test it
function showCallAlerts(callRecord, isInEditMode, callBack) {
var callAlerts = populateCallAlertOnEditCall(callRecord.callAlert);
var callModalInstance = openAlertModalInstance('Call', callAlerts, callBack);
if (callModalInstance !== undefined && callModalInstance !== null) {
callModalInstance.result.then(function() {
// Show equipment alerts based on company details
showEquipmentAlertsBasedOnCompanyDetails(callRecord, isInEditMode, callBack);
});
} else {
// Show equipment alerts based on company details
showEquipmentAlertsBasedOnCompanyDetails(callRecord, isInEditMode, callBack);
}
}
I need to test that each of the functions are called, not worrying about what they do as I'll test them separate, just that they are called.
When populateCallAlertOnEditCall is called it needs to either return an empty array or an array with some items in it
When openAlertModalInstance is called it needs to either return undefined or something that passes through to showEquipmentAlertsBasedOnCompanyDetails
showEquipmentAlertsBasedOnCompanyDetails should actually be called, I'll test that method separate, just that it was called
I have manged to write code to test simple functions but nothing like this one so any help will be much appreciated, I spent most of this afternoon trying to figure it out
You can use jasmine to mock the function calls that you are not interested in testing. For example, you can tell jasmine to return an empty array every time 'populateCallAlertOnEditCall' is called. I will write an example that might give you an insight:
describe('My Test Spec', function() {
var myController;
...
beforeEach( inject(($controller) => {
myController = $controller("myControllerName");
}));
it('Testing showCallAlerts when populateCallAlertOnEditCall returns an empty array', inject(function($controller) {
//setup
//this will replace every call to populateCallAlertOnEditCall with
//the function inside callFake
spyOn(myController, 'populateCallAlertOnEditCall ').and.callFake(function() {
return []; //returning an empty array.
});
//action
myController.showCallAlerts(...);
//assert
//Do your checking here.
}));
it('Testing showCallAlerts when populateCallAlertOnEditCall returns a non-empty array', inject(function($controller) {
//setup
//this will replace every call to populateCallAlertOnEditCall with
//the function inside callFake
spyOn(myController, 'populateCallAlertOnEditCall ').and.callFake(function() {
return [1,2,3,4]; //returning a non-empty array.
});
//action
myController.showCallAlerts(...);
//assert
//Do your checking here.
}));
});
the test that something has been called, you can use a Spy
your assertion would look like:
spyOn(obj, 'populateCallAlertOnEditCall')
expect(obj.method).toHaveBeenCalled()
UPDATED:
populateCallAlertOnEditCall = {}
spyOn(obj, 'populateCallAlertOnEditCall.result')
expect(obj.method).toHaveBeenCalled()
The kind of behaviour you want is called mocking
In Jasmine, mocking is done with Spy Objects, you can read more about those here
Basically, you can use mocks to test if functions were called with the expected parameters.
var xhr = mock( XMLHttpRequest );
xhr.send();
expect( xhr.send ).toHaveBeenCalled();

Categories