Let's say, I have a function:
function consoleOutput(param) {
var newParam = param * param;
console.log(newParam);
}
How can I test in Mocha, that this function will be working correctly (param will be multiplied by two and output to the console). Thanks.
A great library for these types of tests is Sinon. It can be used to "hook" existing functions and track how those functions get called.
For example:
const sinon = require('sinon');
const assert = require('assert');
// the function to test
function consoleOutput(param) {
var newParam = param * param;
console.log(newParam);
}
it('should log the correct value to console', () => {
// "spy" on `console.log()`
let spy = sinon.spy(console, 'log');
// call the function that needs to be tested
consoleOutput(5);
// assert that it was called with the correct value
assert(spy.calledWith(25));
// restore the original function
spy.restore();
});
The advantage of this is that you don't need to change the original function (which, in this case, isn't a big deal, but may not always be possible in larger projects).
Related
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
});
Why do I get a TypeError: expect is not a function when running this test file?
I've installed mocha and chai locally and run the test via yarn run test which runs simply "test": "mocha".
var chai = require('chai')
var expect = chai.expect()
describe('Array', function () {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
expect([1, 2, 3].indexOf(4)).to.be.equal(-1)
})
})
})
When setting expect, you need to do this:
var expect = chai.expect
You are evaluating the function with (), which is not correct according to the documentation
You are assigning the result of calling the function chai.expect to your variable chai, which do not make sense.
Instead, you need to assign a reference to this function, like this:
var expect = chai.expect;
(without the parentheses)
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
}
}
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();
Overview
I've been using the Revealing Module Pattern for a few months and I'm looking for an alternative or variation on this pattern that will solve both issues I'm currently having with event handlers and testability. I know I could come up with some combination of what I have below to solve my problem, but I'm hoping to find a clean alternative that I could use consistently that addresses both of my current concerns.
Revealing Module Pattern
So in this example, I have no issues with event handlers, but I can't mock calls to functions
within functions to test in isolation:
var Lion = (function () {
// Reference to rawr works as expected when the click event is triggered
function watch() {
document.addEventListener('click', rawr);
}
function rawr() {
console.log('rawr');
}
function attack() {
console.log('attack');
}
/*
* Can't test goCrazy() in isolation. Mocking rawr() and attack()
* has no effect as demonstrated below.
*/
function goCrazy() {
rawr();
attack();
// Important "crazy" logic
}
return {
watch: watch,
rawr: rawr,
attack: attack,
goCrazy: goCrazy
};
}());
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var lion = require('Lion');
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Fails
expect(lion.attack).toHaveBeenCalled(); // <-- Fails
// can't test goCrazy() logic in isolation :(
});
});
Same Module using this instead and invoked using new
In this example, I can successfully mock calls to functions within functions, but if I attempt to add an event handler, this becomes undefined when the event is triggered.
var Lion = function () {
// Reference to 'this' becomes undefined when event is triggered
this.watch = function () {
document.addEventListener('click', this.rawr);
}
this.rawr = function () {
console.log('rawr');
}
this.attack = function () {
console.log('attack');
}
/*
* Can successfully test goCrazy() in isolation by being able to mock
* rawr() and attack() as needed
*/
this.goCrazy = function () {
this.rawr();
this.attack();
// Important "crazy" logic
}
};
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var Lion = require('Lion');
var lion = new Lion();
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Success
expect(lion.attack).toHaveBeenCalled(); // <-- Success
// testing goCrazy logic in isolation :)
});
});
Thanks for your time. If any clarification is necessary, let me know and I'll modify my post.
The actual problem here is that, the event handler loses the context of the current object. You can bind it like this
document.addEventListener('click', this.rawr.bind(this));
This will make sure that whenever the rawr is invoked, the this inside rawr corresponds to the lion object which you created.