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.
Related
I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder
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).
I have 2 test cases which test the same function just taking 2 different executions paths, so to illustrate:
MyClass.prototype.functionBeingTested = function() {
if (this.check1()) {
this.isCheck1Called = true;
} else if (this.check2()) {
this.isCheck1Called = false;
} else {
...
}
};
My 2 test cases are as follow:
it('should take check1() execution path', function() {
var myClass= new MyClass({}, {}, {});
var check1Stub sinon.stub(MyClass.prototype, 'check1');
check1Stub.returns(true);
myClass.functionBeingTested();
myClass.isCheck1Called.should.equal(true);
});
it('should take check2() execution path', function() {
var myClass= new MyClass({}, {}, {});
var check2Stub sinon.stub(MyClass.prototype, 'check2');
check2Stub.returns(true);
myClass.functionBeingTested();
myClass.isCheck1Called.should.equal(false);
});
Now by default, check1() returns false so I don't stub it in the second test case, but by the time the second case is running, the check1() function stub is still active and causes the second case to enter the execution path of the first case as-well, making the second case test fail.
I understand it's a problem of test running in parallel and the first sinon stub still being used by the first test case, is there anyway I can solve this problem?
At the end of the first test, you should restore the original method (which is always a good thing, to prevent tests from being influenced by previous tests):
check1Stub.restore()
Or, alternatively, you can use a Sinon sandbox to run each test in:
describe('MyClass', function() {
beforeEach(function() {
this.sinon = sinon.sandbox.create();
});
afterEach(function() {
this.sinon.restore();
});
it('should take check1() execution path', function() {
var myClass = new MyClass({}, {}, {});
// `this.sinon` is the sandbox
var check1Stub = this.sinon.stub(MyClass.prototype, 'check1');
check1Stub.returns(true);
myClass.functionBeingTested();
myClass.isCheck1Called.should.equal(true);
});
it('should take check2() execution path', function() {
var myClass = new MyClass({}, {}, {});
var check2Stub = this.sinon.stub(MyClass.prototype, 'check2');
check2Stub.returns(true);
myClass.functionBeingTested();
myClass.isCheck1Called.should.equal(false);
});
});
(See mocha-sinon, which does exactly the same)
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.
I have a Sails.Js controller that looks like this
module.exports = {
confirmID: function(req,res) {
var uid = req.params.id;
User.findOne({id:uid}).exec(function(err,user) {
// ...
});
}
}
where User is a sails-postgres model. I have tried testing it with mocha, sinon and supertest with a test like this
describe('Controller', function() {
var sandbox;
before(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(User, 'findOne');
});
after(function() {
sandbox.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,res) {
sandbox.fakes[0].called.should.be.true;
done();
});
});
});
If I leave it at that it errors out because exec is called on undefined, but I can't seem to stub the nested exec method without either errors or the test hanging. Is there a way to stub a series of method calls such as .find().exec()? Or am I best to just leave this to integration tests where I can test it with an actual database?
Assuming that you really want to stub (not just spy) - you want to control what the query resolves to as opposed to simply knowing whether the query was executed. Here's what I'm using to stub sails/waterline query methods. Something like...
var stubQueryMethod = require('stubQueryMethod');
describe('Controller', function() {
before(function() {
stubQueryMethod(User, 'findOne', {
id: 123,
name: 'Fred Fakes'
});
});
after(function() {
User.findOne.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,user) {
user.should.have.property('name', 'Fred Fakes');
done();
});
});
});
});
Source: https://gist.github.com/wxactly/f2258078d802923a1a0d
For people looking for other options to stub or mock waterline models, I've found the following four options:
stubQueryMethod.js gist - https://gist.github.com/wxactly/f2258078d802923a1a0d
model mock gist - https://gist.github.com/campbellwmorgan/e305cc36365fa2d052a7
weaselpecker - https://github.com/ottogiron/weaselpecker
sails-mock-models - https://github.com/ryanwilliamquinn/sails-mock-models
After evaluating each one, I've decided on sails-mock-models because it is easy to understand and seems the most used sails mocking library according to npm: https://www.npmjs.com/package/sails-mock-models
Hope this helps someone!
Update: I'm still using sails-mock-models, and it is quite easy, but there are a few drawbacks such as it fails to return promises that are taken into a q.all(promiseArray).then() call. If I get around to investigating the other options or find a workaround, I will post it here.
This will only work for queries that use exec and it overloads all exec calls so if you try to return an error and you have, say, a controller with a policy out front, and the policy does a database lookup, you'll likely go into error there prior to hitting the controller code you intended to test.... that can be fixed with stub.onCall(x), but it is still a bit precarious.
Warnings aside, here's how I've done this in the past:
var path = require('path');
var sinon = require('sinon');
var Deferred = require(path.join(
process.cwd(),
'node_modules/sails',
'node_modules/waterline',
'lib/waterline/query/deferred'
));
module.exports = function () {
return sinon.stub(Deferred.prototype, 'exec');
};
Assuming you have the following service, MyService:
module.exports.dbCall = function (id, cb) {
Model.findOne(id).exec(function (err, result) {
if (err) {
sails.log.error('db calls suck, man');
return cb(err, null);
}
cb(null, result);
});
};
You can test the error case like so:
before(function () {
stub = databaseStub();
});
afterEach(function () {
stub.reset();
});
after(function () {
stub.restore();
});
it('should return errors', function (done) {
stub.onCall(0).callsArgWith(0, 'error');
MyService.dbCall(1, function (err, results) {
assert.equal(err, 'error');
assert.equal(results, null);
done();
});
});