How to test javascript function independently with mocha chai and sinon? - node.js

I am new to unit testing and have been reading a few tutorials about this practice with javascript. I will use a silly example to explain my problem.
Let's say John needs to go to school and before knowing if he's ready to go he has to check if he has his bag and his headphones. This would be called with the following function:
john.isReadyToGo;
The implementation of the isReadtToGo() function for a character object is as follows:
characher.isReadyToGo = function() {
return this.hasBag() && this.hasHeadPhones();
}
characher.hasBag = function() {
// return true or false
}
characher.hasHeadPhones = function() {
//return true or false
}
Now, let's say I want to test this function. In unit testing it is recommended to test functions without them being affected by other functions. This means that in this case I would have to test the three functions but the character.isReadyToGo() function would need to have mock values for this.hasBag() and this.hasHeadPhones(). Am I right?
If so, could you give me a hint on how I can mock these two values?

Here's an example:
let character = {};
character.isReadyToGo = function() {
return this.hasBag() && this.hasHeadPhones();
}
character.hasBag = function() {
// return true or false
}
character.hasHeadPhones = function() {
//return true or false
}
const sinon = require('sinon');
const expect = require('chai').expect;
describe('Is character ready?', () => {
beforeEach(() => {
sinon.stub(character, 'hasBag');
sinon.stub(character, 'hasHeadPhones');
});
afterEach(() => {
character.hasBag.restore();
character.hasHeadPhones.restore();
});
it("Not if they don't have a bag or headphones", () => {
character.hasBag.returns(false);
character.hasHeadPhones.returns(false);
expect(character.isReadyToGo()).to.be.false;
});
it("Not if they have headphones but no bag", () => {
character.hasBag.returns(false);
character.hasHeadPhones.returns(true);
expect(character.isReadyToGo()).to.be.false;
});
it("Not if they have a bag but no headphones", () => {
character.hasBag.returns(true);
character.hasHeadPhones.returns(false);
expect(character.isReadyToGo()).to.be.false;
});
it("Yes, if they have a bag and headphones", () => {
character.hasBag.returns(true);
character.hasHeadPhones.returns(true);
expect(character.isReadyToGo()).to.be.true;
});
});
For each test, this stubs character.hasBag and character.hadHeadphones (this is done in beforeEach). This basically replaces the original with a new function (the stub) that you can control.
Depending on the test, the stub is "told" what to return for each function (using .returns()), isReadyToGo is called, and its result is checked against the expectation.
After each test, the stub is restored (meaning that the original function is restored).

Related

How to use wrapper with beforeEach and afterEach?

Every test I made with jest it's begining with await app.transaction(async(trx) => { and ends with ..rollback..
await app.transaction(async(trx) => {
const a = await update();
expect(a).toBe(something);
await trx.rollback();
});
The actual test is:
const a = await update();
expect(a).toBe(something);
And I want instead of write this wrapper for every test function, just to write within the beforeEach and afterEach.
Since the test is inside of the parameter to transaction you can't really do this in a beforeEach since it will differ based on the test. However you can avoid duplicating the code in each test by writing a helper function like this:
async function wrapper(testFn) {
return app.transaction(async(trx) => {
await testFn();
return trx.rollback();
}
}
// then do this in each test:
it('should work', () => {
await wrapper(async () => {
const a = await update();
expect(a).toBe(something);
});
});

jest mocking functions without passing as a callback

I have some code like:
module.exports = {
idCheck: function(errors) {
errors.some( (error) => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
this._recordError('IDCHECK');
}
});
}
};
I am trying to test it using jest with this:
const IDCheck = require(
'./IDCheck'
);
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
jest.spyOn(this, '_recordError');
IDCheck.idCheck(errors);
});
});
however, when running jest, I get
Cannot spy the _recordError property because it is not a function; undefined given instead
Is there a way of mocking, testing for _recordError() having been called, or not called and with the correct parameter, without passing _recordError through as a parameter?
A few things about this line: jest.spyOn(this, '_recordError');
this has to be IDCheck because there is no this in scope since you are using arrow functions that inherit this if previously set (which it isn't). You can console.log(this) right above the line to prove that point.
'_recordError' is not a method of IDCheck. spyOn checks the target's methods, not methods called within it. Now if _recordError is a method of IDCheck, then you should be ok.
Finally, you basically have to return the data you want in order to verify it. There's no real way to check what was passed unless you return it.
Here's a solution I came up with that does not include some fixes you'd have to implement to fix the potential workflow flaws.
const IDCheck = {
idCheck: function(errors) {
return errors.map(error => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
return this._recordError('IDCHECK')
}
})
},
_recordError: function(data) {
return data
}
}
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
const spy = jest.spyOn(IDCheck, '_recordError')
const check = IDCheck.idCheck(errors).includes('IDCHECK')
expect(spy).toHaveBeenCalled()
expect(check).toBe(true)
});
});

Testing a callback nested in a promise

tl;dr I need to test that my method adds a row to a spreadsheet on successful load of a Google spreadsheet.
saveDataToGoogleSpreadSheet(conversationData){
return new Promise((resolve, reject) => {
Spreadsheet.load(this.getGoogleAPISettings(), (err, spreadsheet) => {
if (err) {
return reject(err);
}
return spreadsheet.receive((receivedError, rows, info) => {
if (receivedError) {
return reject(receivedError);
}
const rowData = this.getSpreadsheetRowData(conversationData, info.totalRows);
spreadsheet.add(rowData);
return spreadsheet.send(sendError => (sendError ? reject(sendError) : resolve()));
});
});
});
}
I tested the case one and case two of the function (the two first errors) but I couldn't do it for the last one, the case of success where we an add a row to a spreadsheet.
I need some help with the structure of the test, or a hint on how could my test be.
Edit: how the previous tests were made
it('should add a row to a Google Spreadsheet', (done) => {
nock('https://spreadsheets.google.com')
.post('/feeds/cells/1ZOd7Sysc-JNa-D5AHb7ZJkwBRMBGaeKpzIwEl7B8RbQ/1/private/full/batch')
.replyWithError({ message: 'abcde' });
api.saveDataToGoogleSpreadSheet({ data: 'some data' })
.then(() => done(new Error('should not have made the call')))
.catch((err) => {
expect(err).to.equal('Error Reading Spreadsheet');
done();
});
}).timeout(4000);
It is hard to tell what is the problem with the test from the little code you have, and no background on what the various objects are and how they come to be, so I will just assume that Spreadsheet is an object that is created through a library you require, and other than that, all other objects are created by the module. i.e. I assume you somewhere have a line resembling something like this:
const Spreadsheet = require('some-google-docs-spreadsheet-lib');
That means one problem is finding out how to control the Spreadsheet object so we can stub out its behavior.
Just to start you out, you might get some good pointers on general code and test structure for easy testing from these two answers, as they cover the two most relevant techniques: dependency injection and exploiting link seams.
Mocking Redis Constructor with Sinon
How to test an ES6 class that needs jquery?
For all I know, you might already utilize one of these techniques, as you say you have been able to test the two error situations. But maybe you have not really been unit testing and done the actual network calls to the service instead (which is more of an integration test)? Anyway, I'll assume no more than what I wrote above and show you how to do the testing using proxyquire:
const assert = require('assert');
const dummy = ()=> {};
const SpreadSheetStubLibrary = { load: dummy };
const MyClassToTest = proxyquire('../src/my-module', {
'some-google-docs-spreadsheet-lib': SpreadSheetStubLibrary
})
const config = {};
const conversationData = {};
let stubSetup;
let spreadsheet;
let myObj;
function setupStubs() {
stubSetup = stubSpreadsheetLoadFunction();
spreadsheet = stubSetup.spreadsheet;
SpreadSheetStubLibrary.load = stubSetup.load;
myObj = new MyClassToTest(config);
conversationData = {};
};
function createSpreadsheetStubObj(){
return {
receive: sinon.stub(),
add: sinon.stub(),
send: sinon.stub()
}
}
function stubSpreadsheetLoadFunction(){
const spreadsheet = createSpreadsheetStubObj();
return {
load: (settings, cb) => cb(null, spreadsheet),
spreadSheetStubObj: spreadsheet
};
}
it('should add a row to the spreadsheet on successful load', () => {
// Arrange
setupStubs();
const rowData = { foo: 1, bar: 2};
spreadsheet.receive.yields(); // calls any callback given
myObj.getSpreadsheetRowData = () => rowData; // see lines below
// if you want to use the real getSpreadsheetRowData, uncomment these lines
//const rows = []; // not used in the method?
//const info = { totalRows : 100 };
//spreadsheet.receive.yields(null, rows, info);
// Act
return myObj.saveDataToGoogleSpreadSheet(conversationData).then(()=>{
// Assert
assert(spreadsheet.add.calledOnce);
assert(spreadsheet.add.calledWith(rowData));
});
});
it('should add a row to the spreadsheet on successful load', () => {
// reuse the above
});
See the Sinon docs for the stub API.
Disclosure: I am part of the Sinon maintainer team.

Testing method signatures in Node.js

I'm relatively new to Unit-testing and TDD specificly and am about to start my first project with TDD using mocha and chai.
Am I supposed to test the existence and parameter length of the methods?
And if so, is there any better way of doing it than I currently am? It feels extremly verbose, especially when repeating this for most of my classes.
For understand I've set up some dummy test.
test/index.js
'use strict';
const assert = require('chai').assert;
const Test = require('../lib/index.js');
describe('Test', function() {
it('should be a function without parameters', function() {
assert.isFunction(Test);
assert.lengthOf(Test, 0);
});
let test;
beforeEach(function() {
test = new Test();
});
describe('static#method1', function() {
it('should have static method method1 with 1 parameter', function() {
assert.property(Test, 'method1');
assert.isFunction(Test.method1);
assert.lengthOf(Test.method1, 1);
});
it('should assert on non-string parameters', function() {
const params = [
123,
{},
[],
function() {}
];
params.forEach(function(param) {
assert.throws(function() {
Test.method1(param)
});
});
});
it('should return "some value"', function() {
assert.equal(Test.method1('param'), 'some value')
});
});
describe('method2', function() {
it('should have method method2 with 2 parameters', function() {
assert.property(test, 'method2');
assert.isFunction(test.method2);
assert.lengthOf(test.method2, 2);
});
it('should assert on non-number parameters', function() {
const params = [
'some string',
{},
[],
function() {}
];
params.forEach(function(param) {
assert.throws(function() {
test.method2(param)
});
});
});
it('should add the parameters', function() {
assert.equal(test.method2(1, 2), 3);
assert.equal(test.method2(9, -2), 7);
assert.equal(test.method2(3, -12), -9);
assert.equal(test.method2(-7, -5), -12);
})
});
});
And the tested implementation.
lib/index.js
'use strict';
const assert = require('chai').assert;
exports = module.exports = (function() {
class Test {
static method1(param0) {
assert.typeOf(param0, 'string');
return 'some value';
}
method2(param0, param1) {
assert.typeOf(param0, 'number');
assert.typeOf(param1, 'number');
return param0 + param1;
}
}
return Test;
}());
No, such detailed tests are not necessary. What is the value of them? What do they help you to achieve?
Usually when testing functions we test behavior of a function, not its implementation. Implementation can completely change without changing the observable behavior: for example, you can find more readable way to rewrite your code or a more performant algorithm.
You test the call signature of your function by the whole set of tests for it, indirectly. Every test provides an example of how to use your function, thus ensuring its call signature and return parameters.

How can I stub a Promise such that my test can be run synchronously?

I am trying to unit test a module by stubbing one of its dependencies, in this case the UserManager
A simplified version of the module is as follows:
// CodeHandler
module.exports = function(UserManager) {
return {
oAuthCallback: function(req, res) {
var incomingCode = req.query.code;
var clientKey = req.query.key;
UserManager.saveCode(clientKey, incomingCode)
.then(function(){
res.redirect('https://test.tes');
}).catch(function(err){
res.redirect('back');
}
);
}
};
};
I'm stubbing the UserManager's saveCode function which returns a Promise such that it returns a resolved Promise, but when I assert that res.redirect has been called, alas at the time of the assertion res.redirect has not yet been called.
A simplified version of the unit test is:
// test
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var res = {
redirect: function() {}
};
var expectedUrl = 'https://test.tes';
var ch;
beforeEach(function() {
sinon.stub(UserManager, 'saveCode').returns(
new RSVP.Promise(function(resolve, reject){
resolve();
})
);
sinon.stub(res, 'redirect');
ch = CodeHandler(UserManager);
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
});
How can I properly stub the promise such that the method under test behaves synchronously?
I've worked out a solution using sinon-stub-promise.
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var ch;
var promise;
var res = {
redirect: function() {}
};
beforeEach(function() {
promise = sinon.stub(UserManager, 'saveCode').returnsPromise();
ch = CodeHandler(UserManager);
sinon.stub(res, 'redirect');
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
describe('can save code', function() {
var expectedUrl = 'https://test.tes';
beforeEach(function() {
promise.resolves();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
});
});
describe('can not save code', function() {
var expectedUrl = 'back';
beforeEach(function() {
promise.rejects();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
})
});
This works perfectly.
Well, the easiest thing would be not to stub it to run synchronously at all since that might change execution order and use Mocha's built in promises support (or jasmine-as-promised if using jasmine).
The reason is there can be cases like:
somePromise.then(function(){
doB();
});
doA();
If you cause promises to resolve synchronously the execution order - and thus output of the program changes, making the test worthless.
On the contrary, you can use the test syntax:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () => {
return methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
});
});
You can use the even lighter arrow syntax for single lines which makes the test even less verbose:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () =>
methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
);
});
In RSVP, you can't set the scheduler as far as I know so it's quite impossible to test things synchronously anyway, other libraries like bluebird let you do it at your own risk, but even in libraries that let you do it it's probably not the best idea.

Resources