Expected a spy, but got Function - javascript

I am trying to implement a test (1) for this module (2).
My purpose is to check if the collection is fetched when a particular event is triggered.
As you can see from my comment in (2) I get the message Error: Expected a spy, but got Function.
The module works but the test fails. any ideas?
(1)
// jasmine test module
describe('When onGivePoints is fired', function () {
beforeEach(function () {
spyOn(this.view.collection, 'restartPolling').andCallThrough();
app.vent.trigger('onGivePoints');
});
it('the board collection should be fetched', function () {
expect(this.view.collection.restartPolling).toHaveBeenCalled();
// Error: Expected a spy, but got Function.
});
});
(2)
// model view module
return Marionette.CompositeView.extend({
initialize: function () {
this.collection = new UserBoardCollection();
this.collection.startPolling();
app.vent.on('onGivePoints', this.collection.restartPolling);
},
// other code
});

You need to get into the actual method, which in this case is on the prototype.
describe('When onGivePoints is fired', function () {
beforeEach(function () {
spyOn(UsersBoardCollection.prototype, 'restartPolling').andCallThrough();
app.vent.trigger('onGivePoints');
});
it('the board collection should be fetched', function () {
expect(UsersBoardCollection.prototype.restartPolling).toHaveBeenCalled();
});
});
Spying on the prototype is a nice trick you can use when you can't get to the actual instance you want to spy on.

I was also getting the same issue but I resolved it by passing an argument in the function call. Then you have to write your test case like this in the it
var data = {name:"test"}
spyOn(UsersBoardCollection.prototype, "restartPolling").and.callThrough();
UsersBoardCollection.prototype.restartPolling(data);
expect(UsersBoardCollection.prototype.restartPolling).toHaveBeenCalled();

I had this bug because I had two versions of sinon loaded, or possibly i wasn't initialising sinon-jasmine correctly. When I explicitly loaded sinon and then sinon jasmine in my spec setup, it started running correctly.

Related

Testing Javascript function using Jasmine

I have a problem when I'm trying to test some javascript, I'm not sure that the code is actually testable, but then I would be pleased to know how to make it testable.
$("#Button").on('touchstart', () => testFunc());
function testFunc() {
button(true, userInfo, gameCode);
}
I want to test when the button is touched that the testFunc is called. I'm using Jasmine as my test framework.
But for whatever reason when I try to call the testFunc from Jasmine just to test that something is working I get the error
"ReferenceError: Can't find variable: testClass in file:///E:/Dokumenter/SemesterProjekt%20fun%20stuff/PRJ4Web/TankWebApplication/TankWebApplication/Test/Test.js (line 8)s"
I have made the reference to the file. So I'm not sure what is going on.
The test code that gives me the error
describe("Joystick test for functionallity",
function() {
beforeEach(function() {
});
it("All default values",
function() {
testFunc();
});
});
How do I test this code? Is it possible to do?
Your goal is to see if the function will be called upon button click, therefore, you must check the actual function call with
toHaveBeenCalled jasmine method.
Invoking the functions by itself doesn't make any sense (like in your example)
You should do something like this:
it('All default values', => {
spyOn(class.testFunc);
document.getElementById('Button').click();
expect(class.testFunc).toHaveBeenCalled();
}));
})

Jasmine Unit Testing: How can I spy on Function.prototype.call() in a function parameter that is passed to my method under test?

I want to use jasmine to test a function that takes in a Function parameter
var functionUnderTest = function(functionParameter) {
functionParameter.call();
}
functionParameter above is a Javascript Function and the call method is the method defined on the Function.prototype. How can I spy on the call property to make sure it's called?
The following doesn't seem to be working
it("should call the functionParameter passed to it", function() {
var mockFunction = function() {};
spyOn(mockFunction, 'call');
functionUnderTest(mockFunction);
expect(mockFunction.call).toHaveBeenCalled();
}
as it results in an:
Error: Expected spy call to have been called.
Presumably because the jasmine spy has it's own call() function.
I also tried this, but it isn't working either:
it("should call the functionParameter passed to it", function() {
var mockFunction = function() {};
spyOn(mockFunction.prototype, 'call');
functionUnderTest(mockFunction);
expect(mockFunction.prototype.call).toHaveBeenCalled();
}
this time, the error is:
TypeError: Cannot read property 'slice' of undefined
with a callstack pointing to:
Expectation.toHaveBeenCalled
Is what I'm trying to do possible? I've been at this for hours and it seems like I'm missing something fundamental in my understanding of jasmine.
As suggested by Bergi, the following works:
it("should call the functionParameter passed to it", function() {
var mockFunction = jasmine.createSpy('mockFunction')
functionUnderTest(mockFunction);
expect(mockFunction).toHaveBeenCalled();
}

Stub save Instance Method of Mongoose Model With Sinon

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.

Javascript: Mocking Constructor using Sinon

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

Jasmine jQuery: loadFixtures() "undefined"?

My Set-up
Win7/64bit
Wamp Server
SublimeText 2
What I did...
Loaded jasmine via ruby (jasmine init)
Ran rake (rake jasmine)
deleted all the default public javascripts, spec files and helper files.
added jquery and jasmine-jquery to the "helpers" directory
Opened a browser, fired up liveReload and ran the following tests...
... of which, only "readFixtures()" passed. All others fail. wtf? Please advise!
"readFixtures()" test works perfectly...
describe("test read fixtures", function() {
it("should be able to read fixtures", function() {
// expect(readFixtures()).toBeDefined();
expect(readFixtures()).toBeDefined();
});
});
"loadFixtures()" test returns "Expected undefined to be defined."
describe("test load fixtures", function() {
it("should be able to load fixtures", function() {
// expect(loadFixtures()).toBeDefined();
expect(loadFixtures()).toBeDefined();
});
});
"setFixtures()" test returns "Expected undefined to be defined."
describe("test set fixtures", function() {
it("should be able to set fixtures", function() {
// expect(setFixtures()).toBeDefined();
expect(setFixtures()).toBeDefined();
});
});
I think you are not testing what you want to here. The functions setFixtures and loadFixtures do not have a return value. What that means is that when you call setFixtures() it will always return undefined. You want to test that the functions are defined, not the functions' return values. Your tests should look like this instead:
it("should be able to set fixtures", function() {
expect(setFixtures).toBeDefined(); // Notice I took out the ()
});

Categories