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.
Related
Suppose that I have a test file foo.test.ts that looks like this:
describe("foo", function() {
let myMock;
beforeAll(function() {
myMock = jest.spyOn(someObject, "someMethod");
});
test("my test", function() {
beforeEach(function() {
myMock.mockReturnValue("someValue");
});
...
});
afterAll(function() {
myMock.mockRestore();
});
});
I'm using this pattern because I've seen it recommended in other StackOverflow answers. I wish to understand how Jest works though.
Do I really need to store the mock in a variable, or can I do this?
describe("foo", function() {
test("my test", function() {
beforeEach(function() {
jest.spyOn(someObject, "someMethod").mockReturnValueOnce("someValue");
});
...
});
});
Notice that I'm using mockReturnValueOnce instead of mockReturnValue.
I ran both implementations and they work fine, but I wonder whether I can say that they are equivalent? Will the "someMethod" function, part of "someObject", not be retained as a mock across other tests from other files?
jest.spyOn() returns a jest mock function and you want to store the returned mock function in a variable so that you can make assertions.
If you don't store the mock function returned by jest.mock(), then how will you make assertions? All the methods, provided by jest, that can be called on a mock function, can't be called without having a reference to the mock function.
If you just want to mock a return value and not make any assertions on the mocked function, then you don't need to save the reference. Generally, when you mock a function, you want to make assertions like how many times it was called, what arguments it was called with, etc.
Will the "someMethod" function, part of "someObject", not be retained
as a mock across other tests from other files?
someMethod will only be mocked in the test file where you mock it. It won't affect the other test files.
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 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.
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