I'm using Jasmine and sinon to test some node.js code. I'm using sinon stubs in this example.
I am trying to stub out a SOAP call to an external service, and I'm wondering if there's any way to ensure that the correct argument is being used in the call.
I have successfully checked if functions are returning the correct arguments in other circumstances, but unfortunately, this scenario is within a nested callback, so I'm not sure how to do it.
Here's my code snippet (I'm trying to test that "client.ExternalService.HttpPort.actualCall" is being called with the "args" variable I am expecting):
class ExternalServiceCaller extends BaseService {
constructor(util) {
super(util);
this.util = util;
}
callExternalService(body, callback){
let url = this.util.config.get('blah.my.url');
let args = {
'request':{
'Property1': body.Property1,
'Property2': body.Property2,
'Property3': body.Property3,
'Property4': body.Property4
}
};
//soap.request()
soap.createClient(url, sslOptions, function(err, client) {
//client.[wsdlName].[binding name].[operation]
client.ExternalService
.HttpPort
.actualCall(args, function(err, result) {
if(!err){
callback(null, result);
}
}, sslOptions);
});
}
}
As I said above, I'm trying to write a test to make sure that actualCall is using the expected "args" variable (making sure that the incoming body is being formatted correctly to be passed to the external call). I can do this pretty easily for the url by stubbing out soap.createClient and using sinon.assert.calledWith() like below:
describe('The function', function(){
let service;
let externalServiceStub;
let externalRequest = helper.myExternalRequestObject;
describe('should use the correct URL',function(){
beforeEach(function(){
service = new ExternalServiceCaller(tools);
externalServiceStub = sinon.stub(soap, 'createClient');
});
it ('and uses the correct URL when successful', function(){
let url = tools.config.get('blah.my.url');
service.callExternalService(myExternalRequestObject, callback => {});
sinon.assert.calledWith(externalServiceStub, url);
externalServiceStub.restore();
});
});
Unfortunately, I have no idea how to check to see that actualCall is being called with the "args" variable that I'm expecting. I could use a fake object to check it against, but I'm not sure how exactly to do that check in the first place in this scenario.
I looked into soap stub, but there isn't a whole lot of documentation and the example didn't make sense to me.
Any help would be greatly appreciated. :)
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.
Premise: JS ES6, NodeJS
Testing Framework: TAP
Mocking Library: testdouble.js
I am attempting to mock the return value for the method of my class and keep receiving this error:
not ok Unsatisfied verification on test double. Wanted: - called with (true). But there were no invocations of the test double.
Here is my testing code:
// Imports for unit testing
const tap = require('tap');
const Subject = require('../src/iTunesClient.js');
const td = require('testdouble');
let reqJson;
// Ensure the iTunes class methods are called
tap.test('iTunesClient class methods function as intended', (t) => {
t.beforeEach((ready) => {
reqJson = td.replace('../src/reqJson.js');
ready();
});
t.afterEach((ready) => {
td.reset();
ready();
});
t.test('iTunesClient.getData', (assert) => {
const callback = td.function();
const subject = new Subject();
subject.setTerm('abc 123');
subject.setURL();
td.when(reqJson.get(td.callback)).thenCallback(true);
subject.getData(callback);
td.verify(callback(true));
assert.end();
});
t.end();
});
Specifically, this line is related to my issue:
td.verify(callback(true));
How can I fake the callback value of true for reqJson.get()? Right now, Subject.geData() is a method of the iTunesClient class which calls another file, reqJson.js, to use its exported get() method.
It's a little hard to tell from your example, but it looks like you're requiring iTunesClient before you call td.replace. In this case, the real reqJson module will be required and cached on line 3.
You need to call td.replace early enough to avoid this, e.g. in between requiring tap and iTunesClient.
I wanted to update this question, as I recently solved this issue. Essentially, I had two issues:
Account for both reqJson function parameters
Account for all callback return values
Per testdouble documentation for item 1:
When passed td.matchers.anything(), any invocation of that test double function will ignore that parameter when determining whether an invocation satisfies the stubbing.
Hence, I adjusted my line of code as follows:
Before: td.when(reqJson.get(td.callback)).thenCallback(true);
After: td.when(reqJson.get(td.matchers.anything(), td.callback)).thenCallback(null, null, null);
I use parse.comas backend service for my iOSapp. I was trying to do everything in objective-c since I don't have any experience with JavaScript, but turns out I will need to develop some Cloud Code Functions in JavaScript to complete my app.
A simple routine I'm trying to create:
User retrieves an object using a Cloud Function.
User saves another object in a different class.
An afterSavefunction runs in the cloud to update object first retrieved.
Now, here is my code:
var UserConfigOrientador = Parse.Object.extend("UserConfigOrientador");
var query = new Parse.Query(UserConfigOrientador);
Parse.Cloud.define('pegarOrientadorLivre', function(request, response) {
Parse.Cloud.useMasterKey();
query.greaterThan("entrevistasDisponibilidade", 0);
query.first({
success: function(results) {
response.success(results);
query = results;
},
error: function(error) {
response.error('Favor, checar rede e tentar novamente.');
}
});
});
// AfterSave
Parse.Cloud.afterSave("Agenda", function(request) {
Parse.Cloud.useMasterKey();
query.set("entrevistasDisponibilidade", 70);
query.save();
}
});
});
Now, the second function is not working, I'm getting the message that Object has no set method.
Basically, my questions are:
How can I share data between functions?
Should I keep everything in main.js or can I use another file?
I'm using webStorm for development. And the question about main.js is that after a while I will have a lot of functions and I am trying to figure out how to organize my code.
Your issue is one of scope, and poorly named variables that you're reusing for multiple purposes.
You define your query variable as a query, use it, but inside the success handler you set it to the result of the query (you now have a variable called query which is actually an instance of your UserConfigOrientador class).
When that Cloud Code finishes running, the result goes out of scope and is most likely set to undefined. You shouldn't be trying to share variables between multiple Cloud Code methods like that.
Is there something on the Agenda object that can let you know which UserConfigOrientador to update? Perhaps you could add a pointer property to the UserConfigOrientador? If you did, then you could use the following:
// AfterSave
Parse.Cloud.afterSave("Agenda", function(request) {
Parse.Cloud.useMasterKey();
var userConfigOrientadorQuery = new Parse.Query("UserConfigOrientador");
// read "userConfigOrientador" pointer property on "Agenda" object
var userConfigId = request.object.get("userConfigOrientador").id;
userConfigOrientadorQuery.get(userConfigId, {
success: function(userConfigOrientador) {
userConfigOrientador.set("entrevistasDisponibilidade", 70);
userConfigOrientador.save();
}
});
});
Mm.. I don't think it quite works the way you expect.
When your Cloud code runs, your initial var query declaration is indeed available within the scope of your cloud function and afterSave function. However, you're not passing it in correctly. As a matter of fact, I'm a little confused because your query seems to be requesting a UserConfigOrientador object while your afterSave is on an Agenda object.
So there are two different things going on here. Since you don't actually save an agenda object, I'm assuming that your response.success() returns a JSON of the UserConfigOrientador object back to client side at which point you do some manipulation then save the Agenda object based on that result.
At this point, when you save() the Agenda object now the afterSave() function will run but your query value will be the var query = new Parse.Query(UserConfigOrientador); which does not have a set method. This is why you get the error.
I'm not even sure your query = results; line will actually execute as you should be calling it at the END of your sub-routine and it signals to Parse that it is the end.
If you can tell me how you're saving the Agenda object I can probably complete the picture.
EDIT: --- abstracted but maybe this is the pattern you're looking for...
var ObjectA = Parse.Object.extend('ObjectA');
var queryObjectA = new Parse.Query('ObjectA');
Parse.Cloud.define('findObjectX', function(request, response) {
Parse.Cloud.useMasterKey();
// other query options here...
query.first({
// the first() function will return a Parse.Object
success: function(objectX) {
// Now you have objectX
// Now you want to save some other object
var otherObj = new ObjectA();
// Do things to otherObj
otherObj.save({
success: function(result) { // will be the saved otherObj
// Now you do stuff to your queried obj and save
objectX.set('something', result); // or whatever
// Note, it accomplishes what I think you want without afterSave()
}
}); // async... but we can just let this guy go
},
error: function(error) {
response.error('Favor, checar rede e tentar novamente.');
}
});
});
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.