I have a Sails.Js controller that looks like this
module.exports = {
confirmID: function(req,res) {
var uid = req.params.id;
User.findOne({id:uid}).exec(function(err,user) {
// ...
});
}
}
where User is a sails-postgres model. I have tried testing it with mocha, sinon and supertest with a test like this
describe('Controller', function() {
var sandbox;
before(function() {
sandbox = sinon.sandbox.create();
sandbox.stub(User, 'findOne');
});
after(function() {
sandbox.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,res) {
sandbox.fakes[0].called.should.be.true;
done();
});
});
});
If I leave it at that it errors out because exec is called on undefined, but I can't seem to stub the nested exec method without either errors or the test hanging. Is there a way to stub a series of method calls such as .find().exec()? Or am I best to just leave this to integration tests where I can test it with an actual database?
Assuming that you really want to stub (not just spy) - you want to control what the query resolves to as opposed to simply knowing whether the query was executed. Here's what I'm using to stub sails/waterline query methods. Something like...
var stubQueryMethod = require('stubQueryMethod');
describe('Controller', function() {
before(function() {
stubQueryMethod(User, 'findOne', {
id: 123,
name: 'Fred Fakes'
});
});
after(function() {
User.findOne.restore();
});
describe('GET /confirmid/:id', function() {
it('should do something', function(done) {
request(sails.hooks.http.app)
.get('/confirmid/123')
.expect(200)
.end(function(err,user) {
user.should.have.property('name', 'Fred Fakes');
done();
});
});
});
});
Source: https://gist.github.com/wxactly/f2258078d802923a1a0d
For people looking for other options to stub or mock waterline models, I've found the following four options:
stubQueryMethod.js gist - https://gist.github.com/wxactly/f2258078d802923a1a0d
model mock gist - https://gist.github.com/campbellwmorgan/e305cc36365fa2d052a7
weaselpecker - https://github.com/ottogiron/weaselpecker
sails-mock-models - https://github.com/ryanwilliamquinn/sails-mock-models
After evaluating each one, I've decided on sails-mock-models because it is easy to understand and seems the most used sails mocking library according to npm: https://www.npmjs.com/package/sails-mock-models
Hope this helps someone!
Update: I'm still using sails-mock-models, and it is quite easy, but there are a few drawbacks such as it fails to return promises that are taken into a q.all(promiseArray).then() call. If I get around to investigating the other options or find a workaround, I will post it here.
This will only work for queries that use exec and it overloads all exec calls so if you try to return an error and you have, say, a controller with a policy out front, and the policy does a database lookup, you'll likely go into error there prior to hitting the controller code you intended to test.... that can be fixed with stub.onCall(x), but it is still a bit precarious.
Warnings aside, here's how I've done this in the past:
var path = require('path');
var sinon = require('sinon');
var Deferred = require(path.join(
process.cwd(),
'node_modules/sails',
'node_modules/waterline',
'lib/waterline/query/deferred'
));
module.exports = function () {
return sinon.stub(Deferred.prototype, 'exec');
};
Assuming you have the following service, MyService:
module.exports.dbCall = function (id, cb) {
Model.findOne(id).exec(function (err, result) {
if (err) {
sails.log.error('db calls suck, man');
return cb(err, null);
}
cb(null, result);
});
};
You can test the error case like so:
before(function () {
stub = databaseStub();
});
afterEach(function () {
stub.reset();
});
after(function () {
stub.restore();
});
it('should return errors', function (done) {
stub.onCall(0).callsArgWith(0, 'error');
MyService.dbCall(1, function (err, results) {
assert.equal(err, 'error');
assert.equal(results, null);
done();
});
});
Related
I am using protractor with Jasmine in windows 10 OS. This is my page for stack over flow login .May I know how to access txt_username inside of clearme: function().
pgstackover.js
//pgstackover.js
'use strict';
module.exports = {
txt_username: element(by.id('email')),
txt_password: element(by.id('password')),
btn_submit: element(by.id('submit-button')),
clearme: function() {
txt_username.isDisplayed();
txt_username.clear();
txt_password.clear()
} ,
go: function() {
browser.driver.ignoreSynchronization = true;
browser.waitForAngularEnabled(false);
browser.driver.manage().window().maximize();
browser.driver.get('https://stackoverflow.com/users/login'); //overrides baseURL
}
This is my spec.js file and it works until clear me function and fails with error Failed: txt_username is not defined
var toDoPage = require('../../../../pages/pgstackover.js');
describe('My first non angular class', function() {
it('should navigate to stack overflow login page', function() {
toDoPage.go();
});
it ('My function', function() {
// browser.driver.get('https://stackoverflow.com/users/login');
toDoPage.txt_username.sendKeys('6');
toDoPage.txt_password.sendKeys('winchester');
var value=toDoPage.btn_submit.getText();
expect(value).toEqual("Log in");
});
it('clear me', function() {
toDoPage.clearme();
});
})
Too make long story short your code does not know what is txt_username so you need to point it out. You are missing .this in your clearme function.
I haven't used a promise manager for ages (since it is obsolete) so here is a working example using async/await:
async clearme() {
await this.txt_username.isDisplayed();
await this.txt_username.clear();
await this.txt_password.clear();
}
I'm new to mocking in Node. I'm using the library Rewire which seems to be one of the better options. I've run into a issue where I need to mock the same function twice within a method so that they return different results:
const tourId: string = await redis.read(accessToken, false);
if (tourId === null) {
logger.warn('invalid token', { accessToken });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
logger.warn('found an access token', { tourId });
const tourResponse: string = await redis.read(tourId, false);
if (tourResponse === null) {
logger.warn('tour not found', { accessToken, tourId });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
In the above example, I call the function redis.read() twice. In order to properly get the test coverage I want I would need to mock those two calls differently which I'm struggling with. Here is a test case I have so far:
it('returns a 404 error if access token not found in redis', async () => {
service.__set__({
redis: {
read: (accessToken: string): string | null => null,
},
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});
Any tips are appreciated!
Answering my own question here: sinon.stub() has an onCall() method you can use to change the output for the :nth call.
https://sinonjs.org/releases/latest/stubs/#stuboncalln-added-in-v18
it('returns an iTour instance if found in redis', async () => {
const callback = sinon.stub();
callback.onCall(0).returns('12345');
callback.onCall(1).returns('67890');
service.__set__({
redis: {
read: () => callback(),
}
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});
Haven't been able to find any answers to these questions through Twitter or the Mongoose JS Gitter channel and would appreciate some help.
I'm writing an API using a Hapi.JS and Mongoose. I'm using a test database for my integration tests. What I've found though is that if I clear the database after more than one describe block it negatively effects my ability to save and run queries in subsequent describe blocks. I'll leave some annotated code below.
How can I clear the database after each test and not have any race conditions that effect other tests?
'use strict'
//this test will pass when run alone
// it clears the db at the end of it's run
let testConfig = require('../fixtures/fixtures.js')
let User = require('../../models/user.js')
let Bucket = require('../../models/bucket.js')
let BucketFactory = require('../../factories/bucket-factory.js')
let request = require('request')
let bluebird = require('bluebird')
let mongoose = require('mongoose')
mongoose.Promise = bluebird
describe('Buckets API', function() {
it('should get all buckets', function(done){
request.get(`${testConfig.testConfig.testUrl}/buckets`, (err, response, body)=>{
if (err){ throw new Error(err)}
expect(response.statusCode).toBe(200)
done()
})
})
it('should get a bucket by its id', function (done) {
request.get(`${testConfig.testConfig.testUrl}/buckets/${mongoose.Types.ObjectId()}`, (err, response, body)=>{
expect(response.statusCode).toBe(200)
done()
})
});
it('should post it', function (done) {
let testBucket = {name: "Drill", userId: mongoose.Types.ObjectId()}
request.post(`${testConfig.testConfig.testUrl}/buckets`, {json: testBucket}, (err, response, body)=>{
if(err){ throw new Error(err)}
expect(response.statusCode).toEqual(200)
done()
} )
});
// i dont need this afterEach
// but for illustrative purposes it will mess up the latter test
// which will pass if i run it by itself
// but it shouldnt right?
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
})
describe('findByAndUpdate', function () {
beforeEach((done)=>{
// this factory creates and saves a bucket
// i've verified this by checking the test database manually
let newBucket = BucketFactory
done()
})
it('should find and update', function (done) {
Bucket.find({}).exec()
.then((data)=>{
request.put(`${testConfig.testConfig.testUrl}/buckets/${data._id}`, {json: {name: 'Marvel'}}, (err, response, body)=>{
if(err){ throw new Error(err)}
console.log(body)
expect(response.statusCode).toEqual(200)
done()
} )
})
});
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
});
I am trying to unit test a module by stubbing one of its dependencies, in this case the UserManager
A simplified version of the module is as follows:
// CodeHandler
module.exports = function(UserManager) {
return {
oAuthCallback: function(req, res) {
var incomingCode = req.query.code;
var clientKey = req.query.key;
UserManager.saveCode(clientKey, incomingCode)
.then(function(){
res.redirect('https://test.tes');
}).catch(function(err){
res.redirect('back');
}
);
}
};
};
I'm stubbing the UserManager's saveCode function which returns a Promise such that it returns a resolved Promise, but when I assert that res.redirect has been called, alas at the time of the assertion res.redirect has not yet been called.
A simplified version of the unit test is:
// test
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var res = {
redirect: function() {}
};
var expectedUrl = 'https://test.tes';
var ch;
beforeEach(function() {
sinon.stub(UserManager, 'saveCode').returns(
new RSVP.Promise(function(resolve, reject){
resolve();
})
);
sinon.stub(res, 'redirect');
ch = CodeHandler(UserManager);
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
});
How can I properly stub the promise such that the method under test behaves synchronously?
I've worked out a solution using sinon-stub-promise.
describe('CodeHandler', function() {
var req = {
query: {
code: 'test-code',
key: 'test-state'
}
};
var ch;
var promise;
var res = {
redirect: function() {}
};
beforeEach(function() {
promise = sinon.stub(UserManager, 'saveCode').returnsPromise();
ch = CodeHandler(UserManager);
sinon.stub(res, 'redirect');
});
afterEach(function() {
UserManager.saveCode.restore();
res.redirect.restore();
});
describe('can save code', function() {
var expectedUrl = 'https://test.tes';
beforeEach(function() {
promise.resolves();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
});
});
describe('can not save code', function() {
var expectedUrl = 'back';
beforeEach(function() {
promise.rejects();
});
it('redirects to the expected URL', function(){
ch.oAuthCallback(req, res);
assert(res.redirect.calledWith(expectedUrl));
})
})
});
This works perfectly.
Well, the easiest thing would be not to stub it to run synchronously at all since that might change execution order and use Mocha's built in promises support (or jasmine-as-promised if using jasmine).
The reason is there can be cases like:
somePromise.then(function(){
doB();
});
doA();
If you cause promises to resolve synchronously the execution order - and thus output of the program changes, making the test worthless.
On the contrary, you can use the test syntax:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () => {
return methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
});
});
You can use the even lighter arrow syntax for single lines which makes the test even less verbose:
describe("the test", () => { // use arrow functions, node has them and they're short
it("does something", () =>
methodThatReturnsPromise().then(x => {
// assert things about x, throws will be rejections here
// which will cause a test failure, so can use `assert`
});
);
});
In RSVP, you can't set the scheduler as far as I know so it's quite impossible to test things synchronously anyway, other libraries like bluebird let you do it at your own risk, but even in libraries that let you do it it's probably not the best idea.
I have been searching this site and the web for a while now and I cannot find a solution to this problem. I am trying to test the REST function of my API, but the PUT test never seems to work. Each time the test runs in mocha, I get the error "Uncaught assertion error: expected [] to equal {objectData}" where objectData is the json representation of the object I am trying to post (named couponTwo).
I have a feeling the problem lies in the beforeEach function, as it clears the database before each test, which needs to be done for many other tests to run correctly. Here is the test code:
var config = require('../config/config');
var mongoose = require('mongoose');
var should = require('should');
var request = require('supertest');
var Coupon = require('../models/coupon');
var url = require('../config/config').test.url;
process.env.NODE_ENV = 'test';
beforeEach(function (done) {
function clearCollections() {
for (var collection in mongoose.connection.collections) {
mongoose.connection.collections[collection].remove(function() {});
}
return done();
}
if (mongoose.connection.readyState === 0) {
mongoose.connect(config.test.db, function (err) {
if (err) throw err;
return clearCollections();
});
} else {
return clearCollections();
}
});
afterEach(function (done) {
mongoose.disconnect();
return done();
});
Here is the that is supposed to test that an object exists in the database after a PUT:
describe('#post', function () {
it('should return a coupon object after post', function (done) {
request(url).post('/coupons')
.set('Content-Type', 'application/json')
.send(couponTwo)
request(url).get('/coupons').end(function (err, res) {
if (err) throw err;
console.log(res.body);
res.body.should.eql(couponTwo);
done();
})
})
})
I apologize if the answer to this question is obvious and I am missing something fundamental, but I have reached a roadblock. Thanks for your help!
I think it is because of the asynchronous nature of request calls. You need to wrap the second request in a callback, so that it will only be executed when the first one is completed and your test object is put into the database.
Also, .eql(couponTwo) will fail in your case anyway, because your response is an array containing the object that was put, and you compare it directly to the object. Use .eql([couponTwo]) if you want to make sure that it is the only element in the array, or just use .containEql(couponTwo).
Try this:
request(url).post('/coupons')
.set('Content-Type', 'application/json')
.send(couponTwo)
.end(function () {
request(url).get('/coupons').end(function (err, res) {
if (err) throw err;
console.log(res.body);
res.body.should.containEql(couponTwo);
done();
});
});