Is there a way in Mocha to test if a function calls a specific method or external function?
I am using Mocha with Chai, but am open to any other assertion libraries.
Ok, so testing whether a methid is being called is pretty easy using sinon. I'm not sure about testing to see if an external function is being called though. So I updated the examples to represent something a little more "real world". I am working on a node app, so foo.js
and bar.js
are both modules.
var bar = require('bar');
var xyz = function () {};
var Foo = module.exports = function () {
this.bar();
bar();
xyz();
};
Foo.prototype.bar = function () {};
var bar = module.exports = function () {};
var chai = require('chai');
var sinon = require('sinon');
var sinonChai = require('sinonChai');
var expect = chai.expect;
var Foo = require('../lib/foo');
chai.use('sinonChai');
describe('Foo', function () {
var method;
beforeEach(function (done) {
method = sinon.spy(Foo.prototype, 'bar');
done();
});
afterEach(function (done) {
method.restore();
done();
});
it('should call Foo.prototype.bar() immediately', function () {
new Foo();
expect(method).to.have.been.called;
});
it('should call the module bar immediately', function () {
// ????????????
});
it('should call xyz() immediately', function () {
// ????????????
});
});
So as you can see I've figured out how to test for Foo.prototype.bar
, but I can't find a way to implement the second and third tests.
You can call the method from a test class, similar to how you call method from other classes. ClassName classInstanceObj = new ClassName();
var chai = require('chai'); var sinon = require('sinon'); var sinonChai = require('sinonChai'); var expect = chai. expect; var Foo = require('../lib/foo'); chai. use('sinonChai'); describe('Foo', function () { var method; beforeEach(function (done) { method = sinon. spy(Foo.
So this question was really two in one.
Firstly, "how to test if a method is being called": I laid out the code for this in the example, but basically, using sinon.js you just wrap the method in a "spy" which allows you to write a test that expects that spy to have been called.
Secondly, "how to test if a private function(one that was not exported as part of the module) has been called":
Basically, you don't. It is possible to export these functions when in a testing environment and not in production, but this seems a little too hacky to me.
I've come to the conclusion that when calling another module you should just break the TDD cycle and not test for this since it's probably going to be a small amount of code and the module will have already been tested on it's own.
If you are calling a private function that is declared within you're module and want to test it you should write a more broad test that tests for the result of this function being called rather than testing whether the function is being called or what is actually happening within the function.
Here's a really simple example:
var _ = require('lodash');
var Foo = module.exports = function (config) {
this.config = _.merge({
role: 'user',
x: '123',
y: '321'
},
config);
this.config.role = validateRole(this.config.role);
};
var validateRole = function (role) {
var roles = [
'user', 'editor', 'admin'
];
if (_.contains(roles, role)) {
return role;
} else {
return 'user'
}
};
var chai = require('chai');
var expect = chai.expect;
var Foo = require('../lib/foo');
describe('Foo', function () {
it('should set role to \'user\' if role is not valid', function () {
var foo = new Foo({role: 'invalid'});
expect(foo.config.role).to.equal('user');
});
};
I'm using expect
assertion library with Mocha
, but Chai
might have analogous methods
You can test if a function calls a specific method/function using Spies. You did this in you code above.
The problem with the code you are testing is Context. So I will address it in this answer. You can test if an external function is called, but it needs a context, so you might have to change your code.
I'm using
bar
(module) as example. Forxyz
(function) go to the Second method. The explanation is the same for both.
bar
inside an objectvar bar = module.exports = {
bar: function () {};
}
var Foo = module.exports = function () {
bar.bar();
....
};
This way you can spy on it doing:
it('should call the module bar immediately', function () {
//note I'm getting the bar method from the exported object (bar module)
var bar = expect.spyOn(bar, 'bar');
new Foo();
expect(bar).toHaveBeenCalled();
bar
module as Foo's prototype methodIf you don't want to change bar.js
, you may set the required module as a prototype method of Foo. Then you have a context to spy on.
var bar = require('./bar');
var Foo = module.exports = function () {
this.bar();
this.barModule();
};
Foo.prototype.bar = function () {};
Foo.prototype.barModule = bar; // setting here as barModule
it('should call the module bar immediately', function () {
var barSpy = expect.spyOn(Foo.prototype, 'barModule');
new Foo();
expect(barSpy).toHaveBeenCalled();
});
The changes you must do are for changing the context of your variables.
To make it clear:
var bar = require('bar');
var Foo = module.exports = function () {
this.bar();
bar();
};
Foo.prototype.bar = function () {};
In this snippet, you are requiring bar
and later setting this.bar
using Foo.prototype
. So, how can you set 2 variables with the same name and reference each other nicely?
The answer is Context and Scope. Your this.bar
is referencing the bar
variable set in this
context (which points to Foo
). On the other hand, your bar
- note there is no this
- is referencing the bar
variable set in the function's (module) scope.
So, you may test your Foo.prototype.bar
, since it is a module method, has a context and you may spy on it. Buy you can't spy on the required bar
because it is scoped (think of it as private).
Good read: http://ryanmorr.com/understanding-scope-and-context-in-javascript/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With