I am pulling my hair out trying to figure out how to mock a constructor using sinon. I have a function that will create multiple widgets by calling a constructor that accepts a few arguments. I want to verify that the constructor is called the correct number of times with the correct parameters, but I don't want to actually construct the widgets. The following links seemingly explain a straightforward way of mocking the constructor, however it does not work for me:
Spying on a constructor using Jasmine
http://tinnedfruit.com/2011/03/25/testing-backbone-apps-with-jasmine-sinon-2.html
When I make the following call to stub the constructor:
sinon.stub(window, "MyWidget");
I get the following error:
Uncaught TypeError: Attempted to wrap undefined property MyWidget as function
When debugging in Chrome I see MyWidget shows up in the Local section of the Scope Variables, however there is not MyWidget property off of window.
Any help would be greatly appreciated.
I needed a solution for this because my code was calling the new operator. I wanted to mock the object that the new call created.
var MockExample = sinon.stub();
MockExample.prototype.test = sinon.stub().returns("42");
var example = new MockExample();
console.log("example: " + example.test()); // outputs 42
Then I used rewire to inject it into the code that I was testing
rewiredModule = rewire('/path/to/module.js');
rewiredModule.__set__("Example", example);
From the official site of sinonjs:
Replaces object.method with a stub function. The original function can be restored bycalling object.method.restore(); (or stub.restore();). An exception is thrown if the property is not >already a function, to help avoid typos when stubbing methods.
this simply states that the function for which you want to create the stub must be member of the object object.
To make things clear; you call
sinon.stub(window, "MyWidget");
The MyWidget function needs to be within the global scope (since you pass window as parameter). However, as you already said, this function is in a local scope (probably defined within an object literal or a namespace).
In javascript everyone can have access to the global scope, but not the other way around.
Check where you declare the MyWidget function and pass container object as first parameter to sinon.stub()
Using Sinon 4.4.2, I was able to mock an instance method like this:
const testObj = { /* any object */ }
sinon.stub(MyClass.prototype, "myMethod").resolves(testObj)
let myVar = await new MyClass(token).myMethod(arg1, arg2)
// myVar === testObj
A similar solution provided here:
Stubbing a class method with Sinon.js
I used Mockery to Mock a Constructor/Function without any problems.
var mockery = require('mockery');
var sinon = require('sinon');
mockery.enable({
useCleanCache: true,
warnOnReplace: false,
warnOnUnregistered: false
});
exports.Client = function() {/* Client constructor Mock */};
var ClientSpy = sinon.spy(exports, 'Client');
mockery.registerMock('Client', ClientSpy);
var Factory = require('Factory'); // this module requires the Client module
You should be able to apply a Sinon Spy just as the example above does.
Make sure to disable or reset Mockery after the test(s)!
Use sinon.createStubInstance(MyES6ClassName), then when MyES6ClassName is called with a new keyword, a stub of MyES6ClassName instance will returned.
I ran into this error by mistakenly typing sinon.stub.throws(expectedErr) rather than sinon.stub().throws(expectedErr). I've made similar mistakes before and not encountered this particular message before, so it threw me.
Mocking and stubbing the constructor with sinon
I give two solutions. The first addresses the question, how to mock the constructor with behaviour, the second shows how to stub it with a dummy. Google guided me multiple times to this question for the search how to stub it.
Mocking the constructor with behaviour
I don't know if this is the shortest path. At least it does, what was asked for.
First I use sinon's fake method to create a mocking constructor Mock with the behaviour I want. Then I have to add the methods one by one. For reasons I didn't investigate, it did not work, when setting the whole prototype of UnderTest to Mock.
require('chai').should();
const { fake} = require('sinon');
class UnderTest {
constructor() {
this.mocked = false;
}
isMocked() {
return this.mocked;
}
}
describe('UnderTest', () => {
let underTest;
let isMocked;
before(() => {
const Mock = fake(function () { this.mocked = true; });
Mock.prototype.isMocked = UnderTest.prototype.isMocked;
underTest = new Mock();
isMocked = underTest.isMocked();
});
it('should be mocked', () => {
isMocked.should.be.true;
});
});
Stubbing the constructor with a dummy
If you are leaded to this post, because you just want to stub the constructor to keep it from being executed.
Sinon's createStubInstance creates a stubbed constructor. It also stubs all methods. Hence, the method under test has to be restored before.
require('chai').should();
const { createStubInstance } = require('sinon');
class UnderTest {
constructor() {
throw new Error('must not be called');
}
testThis() {
this.stubThis();
return true;
}
stubThis() {
throw new Error('must not be called');
}
}
describe('UnderTest', () => {
describe('.testThis()', () => {
describe('when not stubbed', () => {
let underTest;
let result;
before(() => {
underTest = createStubInstance(UnderTest);
underTest.testThis.restore();
result = underTest.testThis();
});
it('should return true', () => {
result.should.be.true;
});
it('should call stubThis()', () => {
underTest.stubThis.calledOnce.should.be.true;
});
});
});
});
Just found this in the documentation.
If you want to create a stub object of MyConstructor, but don’t want the constructor to be invoked, use this utility function.
var stub = sinon.createStubInstance(MyConstructor)
I was able to get StubModule to work after a few tweaks, most notably passing in async:false as part of the config when requiring in the stubbed module.
Kudos to Mr. Davis for putting that together
Related
I am working on a project where I am observing types of each binding layer function that node.js javascript layer calls. For observing types, I created a stub using sinon that looks something like this
var originalProcessBinding = process.binding;
sinon.stub(process, 'binding').callsFake(function (data) {
var res = originalProcessBinding(data);
// custom code here
return res;
}
So, my idea is to look at each object inside res and see if its a Function. If it is, create a stub that records the state and then call the original Function. The custom code looks something like
_.forEach(res, function(value, key) {
if (_.isFunction(value)) {
sinon.stub(res, key).callsFake(function() {
var args = arguments;
// do some processing with the arguments
save(args);
// call the original function
return value(...arguments);
}
}
}
However, I am not sure if this handles all the types of returns. For instance, how are the errors handled? What happens if the function is asynchronous?
I ran the node.js test suite and found lots of failing test cases. Is there a better way to stub the functions. Thanks.
Edit: The failing test cases have errors in common that look like Callback was already called or Timeout or Expected Error.
Unfortunately, even though many errors can be fixed, It's hard to add sinon to the build process. I implemented by own stubbing methods in vanilla js to fix this. Anyone who's looking to stub internal node.js functions should find this helpful.
(function() { process.binding = function(args) {
const org = process.binding;
const util = require('util');
var that = org(args),
thatc = that;
for (let i in thatc) {
if (util.isFunction(thatc[i]) && (!thatc[i].__isStubbed)) {
let fn = thatc[i];
if (i[0] !== i[0].toUpperCase()) {
// hacky workaround to avoid stubbing function constructors.
thatc[i] = function() {
save(arguments);
return fn.apply(that, arguments);
}
thatc[i].__isStubbed = true;
}
}
}
return thatc;
}
})();
This code passes all the tests with the current master of Node.js. Adding sinon seems to mutate the function object that triggers internal v8 checks which fail.
I'm having issues with Jest testing that a closure (an inner function) has been called after calling the outer function. I've tried using the spyOn with no positive result. This seems to be a relatively simple problem, to which I haven't found any results from googling.
// helper.js
export const bar = () => {}
export const foo = () => {
bar(); //How do I test that this has been called?
}
//helper.test.js
import * as H from 'helper';
const barSpy = jest.spyOn(H, 'bar');
H.foo();
expect(barSpy).toHaveBeenCalled(); // Jest throws error "Expected mock function to have been called."
I had the same issue once, and found this article which is very well explained: https://www.exratione.com/2015/12/es6-use-of-import-property-from-module-is-not-a-great-plan/
From the article:
Stubbing a function in Javascript requires the function to be bound to a context, any context, that is in scope for both the test code and the code being tested. In a sane world this context is provided by the module. For example, in ES5 Node.js:
exports.fn = function () {}
The thing to avoid doing in ES5 is the following, overriding module.exports with a function. All too many people do this and it is inconsiderate, as any module using that code must then take extra steps to be usefully unit tested:
module.exports = function () {}
So the last exported function can't be stubbed since it's not bound to any context, theres a workaraound for that, bounding the function to the exports object of the module you are testing and calling it as exports.importedFunction, like so:
var example = require('./example');
// Exported for test purposes. If we don't do this, then
// example is encapsulated here and cannot be stubbed.
exports.example = example;
// Usage.
exports.invokeExample = function () {
return exports.example();
};
Then you'll be able to spy on it, but you had to write that extra code, which is kinda ugly and not very useful neither clear.
In ES6 using "import { x } from 'y'" is analogous to overwriting module.exports with a function in ES5. The result is that an imported function is encapsulated in the module and cannot be stubbed in unit tests without writing more boilerplate code that would otherwise have been the case.
This also happens for your case: import * as H from 'helper'
I'm trying to use Jasmine to test that some modular JavaScript is initiating as expected. But I'm going round in circles.
As a simplified example, I have these two files:
// myObj.all.js
;(function() {
window.myObj = window.myObj || {};
myObj.all = {
init: function() {
myObj.page.init();
}
};
}());
// myObj.page.js
;(function() {
window.myObj = window.myObj || {};
myObj.page = {
init: function() {
console.log('hello');
}
};
}());
And in the HTML I would call:
myObj.all.init();
to initialise everything. There are more modules in addition to myObj.page, and one call to myObj.all.init() initialises all of them.
I want to test that when myObj.all.init() is called, then myObj.page.init() is also called. I thought something like this would do it, but the test fails:
spyOn(myObj.page, 'init');
myObj.all.init({});
expect(myObj.page.init).toHaveBeenCalled();
How should I test this?
I have never used jasmine but an easy way to verify that methods are being called from within another method is to mock the methods that should be called.
It looks like jasmine has support for mocking.
In this case you could mock myObj.page.init() and all other methods that should be called from myObj.all.init(). The mocking should provide a method to assert that the mocked method was actually called.
I am trying to test a service function I use to save a widget using a Mongoose model. I want to stub out the save instance method on my model, but I cannot figure out a good solution. I have seen other suggestions, but none seem to be complete.
See... this, and this.
Here is my model...
// widget.js
var mongoose = require('mongoose');
var widgetSchema = mongoose.Schema({
title: {type: String, default: ''}
});
var Widget = mongoose.model('Widget', widgetSchema);
module.exports = Widget;
Here is my service...
// widgetservice.js
var Widget = require('./widget.js');
var createWidget = function(data, callback) {
var widget = new Widget(data);
widget.save(function(err, doc) {
callback(err, doc);
});
};
My service is very simple. It accepts some JSON data, creates a new widget, and then saves the widget using the "save" instance method. It then calls back passing an err and doc based on the outcome of the save call.
I only want to test that when I call createWidget({title: 'Widget A'})...
The Widget constructor is called once with the data I passed to the service function
The save instance method on the newly created widget object is called once
EXTRA CREDIT: That the save instance method calls back with null for the err and with {title: 'Widget A'} for the doc.
In order to test this in isolation, I would probably need to...
Mock or stub the Widget constructor so that it would return a mock widget object that I create as part of my test.
Stub the mock widget object's save function so I can control what occurs.
I am having trouble figuring out how to do this with Sinon. I have tried several variations found on the pages of SO with no luck.
NOTES:
I don't want to pass in an already constructed model object to the service because I want the service to be the only thing that "knows" about mongoose.
I know this is not the biggest deal (to just test this with more of an integration or end-to-end test, but it would be nice to figure out a solution.
Thanks for any help you can provide.
If were to test that, this is how I would approach it, first have a way to inject my mocked widget to the widget-service. I know there's node-hijack, mockery or something like node-di, they all have different styles, I'm sure there's more. Choose one and use it.
Once I get that right, then I create my widget-service with my mock widget module. Then I do something like this(this is using mocha btw):
// Either do this:
saveStub = sinon.stub();
function WidgetMock(data) {
// some mocking stuff
// ...
// Now add my mocked stub.
this.save = saveStub;
}
// or do this:
WidgetMock = require('./mocked-widget');
var saveStub = sinon.stub(WidgetMock.prototype, 'save');
diInject('widget', WidgetMock); // This function doesn't really exists, but it should
// inject your mocked module instead of real one.
beforeEach(function () {
saveStub.reset(); // we do this, so everytime, when we can set the stub only for
// that test, and wouldn't clash with other tests. Don't do it, if you want to set
// the stub only one time for all.
});
after(function () {
saveStub.restore();// Generally you don't need this, but I've seen at times, mocked
// objects clashing with other mocked objects. Make sure you do it when your mock
// object maybe mocked somewhere other than this test case.
});
it('createWidget()', function (done) {
saveStub.yields(null, { someProperty : true }); // Tell your stub to do what you want it to do.
createWidget({}, function (err, result) {
assert(!err);
assert(result.someProperty);
sinon.assert.called(saveStub); // Maybe do something more complicated. You can
// also use sinon.mock instead of stubs if you wanna assert it.
done();
});
});
it('createWidget(badInput)', function (done) {
saveStub.yields(new Error('shhoo'));
createWidget({}, function (err, result) {
assert(err);
done();
});
});
This is just a sample, my tests sometimes get more complicated. It happens that most of the time, the backend calling function(here it is, widget.save) that I want to mock, is the one that I want it's behavior to change with every different test, so that's why I reset the stub everytime.
Here's also another example for doing similar thing: https://github.com/mozilla-b2g/gaia/blob/16b7f7c8d313917517ec834dbda05db117ec141c/apps/sms/test/unit/thread_ui_test.js#L1614
Here is how I would do it. I'm using Mockery to manipulate the module loading. The code of widgetservice.js must changed so that it calls require('./widget');, without the .js extension. Without the modification, the following code won't work because I use the general recommended practice of avoiding extensions in require calls. Mockery is states clearly that the names passed to the require call must match exactly so.
The test runner is Mocha.
The code follows. I've put copious comments in the code itself.
var mockery = require("mockery");
var sinon = require("sinon");
// We grab a reference to the pristine Widget, to be used later.
var Widget = require("./widget");
// Convenience object to group the options we use for mockery.
var mockery_options = {
// `useCleanCache` ensures that "./widget", which we've
// previously loaded is forgotten when we enable mockery.
useCleanCache: true,
// Please look at the documentation on these two options. I've
// turned them off but by default they are on and they may help
// with creating a test suite.
warnOnReplace: false,
warnOnUnregistered: false
};
describe("widgetservice", function () {
describe("createWidget", function () {
var test_doc = {title: "foo"};
it("creates a widget with the correct data", function () {
// Create a mock that provides the bare minimum. We
// expect it to be called with the value of `test_doc`.
// And it returns an object which has a fake `save` method
// that does nothing. This is *just enough* for *this*
// test.
var mock = sinon.mock().withArgs(test_doc)
.returns({"save": function () {}});
// Register our mock with mockery.
mockery.registerMock('./widget', mock);
// Then tell mockery to intercept module loading.
mockery.enable(mockery_options);
// Now we load the service and mockery will give it our mock
// Widget.
var service = require("./widgetservice");
service.createWidget(test_doc, function () {});
mock.verify(); // Always remember to verify!
});
it("saves a widget with the correct data", function () {
var mock;
// This will intercept object creation requests and return an
// object on which we can check method invocations.
function Intercept() {
// Do the usual thing...
var ret = Widget.apply(this, arguments);
// Mock only on the `save` method. When it is called,
// it should call its first argument with the
// parameters passed to `yields`. This effectively
// simulates what mongoose would do when there is no
// error.
mock = sinon.mock(ret, "save").expects("save")
.yields(null, arguments[0]);
return ret;
}
// See the first test.
mockery.registerMock('./widget', Intercept);
mockery.enable(mockery_options);
var service = require("./widgetservice");
// We use sinon to create a callback for our test. We could
// just as well have passed an anonymous function that contains
// assertions to check the parameters. We expect it to be called
// with `null, test_doc`.
var callback = sinon.mock().withArgs(null, test_doc);
service.createWidget(test_doc, callback);
mock.verify();
callback.verify();
});
afterEach(function () {
// General cleanup after each test.
mockery.disable();
mockery.deregisterAll();
// Make sure we leave nothing behind in the cache.
mockery.resetCache();
});
});
});
Unless I've missed something, this covers all the tests that were mentioned in the question.
With current version of Mongoose you can use create method
// widgetservice.js
var Widget = require('./widget.js');
var createWidget = function(data, callback) {
Widget.create(data, callback);
};
Then to test the method (using Mocha)
// test.js
var sinon = require('sinon');
var mongoose = require('mongoose');
var Widget = mongoose.model('Widget');
var WidgetMock = sinon.mock(Widget);
var widgetService = require('...');
describe('widgetservice', function () {
describe('createWidget', function () {
it('should create a widget', function () {
var doc = { title: 'foo' };
WidgetMock
.expects('create').withArgs(doc)
.yields(null, 'RESULT');
widgetService.createWidget(doc, function (err, res) {
assert.equal(res, 'RESULT');
WidgetMock.verify();
WidgetMock.restore();
});
});
});
});
Also, if you want to mock chained methods use sinon-mongoose.
I'm using Mocha with Sinon to unit test my node.js modules. I've successfully mocked other dependencies (other modules that I've written), but I've run into problems stubbing non-pure functions (like Math.random() and Date.now()). I've tried the following (simplified so that this question isn't so localized), but Math.random() was not stubbed because of an obvious scope problem. The instances of Math are independent between the test file and mymodule.js.
test.js
var sinon = require('sinon'),
mymodule = require('./mymodule.js'),
other = require('./other.js');
describe('MyModule', function() {
describe('funcThatDependsOnRandom', function() {
it('should call other.otherFunc with a random num when no num provided', function() {
sinon.mock(other).expects('otherFunc').withArgs(0.5).once();
sinon.stub(Math, 'random').returns(0.5);
funcThatDependsOnRandom(); // called with no args, so should call
// other.otherFunc with random num
other.verify(); // ensure expectation has been met
});
});
});
So in this contrived example, functThatDependsOnRandom() would look like:
mymodule.js
var other = require('./other.js');
function funcThatDependsOnRandom(num) {
if(typeof num === 'undefined') num = Math.random();
return other.otherFunc(num);
}
Is it possible to stub Math.random() in this scenario with Sinon?
yes, this is an old question but it is valid. Here is an answer that works, though I'd love to hear suggestions on how to make it better.
The way I've dealt with this in the browser is to create a proxy object. For example, you can't stub the window object in the browser so you can create a proxy object called windowProxy. When you want to get the location you create a method in windowProxy called location that returns or sets windowLocation. Then, when testing, you mock windowProxy.location.
You can do this same thing with Node.js, but it doesn't work quite as simply. The simple version is that one module can't mess with another module's private namespace.
The solution is to use the mockery module. After initializing mockery, if you call require() with a parameter that matches what you told mockery to mock, it will let you override the require statement and return your own properties.
UPDATE: I've created a fully functional code example. It is on Github at newz2000/dice-tdd and available via npm. /END UPDATE
The docs are pretty good, so I suggest reading them, but here's an example:
Create a file randomHelper.js with contents like this:
module.exports.random = function() {
return Math.random();
}
Then in your code that needs a random number, you:
var randomHelper = require('./randomHelper');
console.log('A random number: ' + randomHelper.random() );
Everything should work like normal. Your proxy object behaves in the same way as Math.random.
It is important to note that the require statement is accepting a single parameter, './randomHelper'. We'll need to note that.
Now in your test, (I'm using mocha and chai for example):
var sinon = require('sinon');
var mockery = require('mockery')
var yourModule; // note that we didn't require() your module, we just declare it here
describe('Testing my module', function() {
var randomStub; // just declaring this for now
before(function() {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false
});
randomStub = sinon.stub().returns(0.99999);
mockery.registerMock('./randomHelper', randomStub)
// note that I used the same parameter that I sent in to requirein the module
// it is important that these match precisely
yourmodule = require('../yourmodule');
// note that we're requiring your module here, after mockery is setup
}
after(function() {
mockery.disable();
}
it('Should use a random number', function() {
callCount = randomStub.callCount;
yourmodule.whatever(); // this is the code that will use Math.random()
expect(randomStub.callCount).to.equal(callCount + 1);
}
}
And that is it. In this case, our stub will always return 0.0.99999; You can of course change it.
It is easy to stub Date.now() with sinon by using Fake timers :
Fake timers provide a clock object to pass time, which can also be used to control Date objects created through either new Date(); or Date.now(); (if supported by the browser).
// Arrange
const now = new Date();
const clock = sinon.useFakeTimers(now.getTime());
// Act
// Call you function ...
// Assert
// Make some assertions ...
// Teardown
clock.restore();
Are you sure that not mocking Math is the problem. It seems that this line make not much sense:
sinon.mock(other).expects('otherFunc').withArgs(0.5).once();
you mock others in one module but use it in another one. I dont think that you will get the mocked version in mymodule.js. On the other hand stubbing Math.random should work, as this is global for all modules.
Also take a look at this SO for mocking dependencies in nodeJS tests.