Sails js running tests - node.js

I am trying to run my sails unit tests (using mocha and istanbul)
when running
grunt test
I get the errors
1) "before all" hook
2) "after all" hook
0 passing (5s)
2 failing
1) "before all" hook:
Error: timeout of 2000ms exceeded
at null.<anonymous> (/vagrant/node_modules/mocha/lib/runnable.js:157:19)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
2) "after all" hook:
ReferenceError: sails is not defined
the setup does not seem to find my Sails...but doing
which sails
I get
/usr/local/node/node-default/bin/sails
and running sails lift works fine
Here is the mocha tests file in my project
//boostrap.test.js
var Sails = require('sails');
before(function(done) {
Sails.lift({
// configuration for testing purposes
}, function(err, sails) {
if (err) return done(err);
// here you can load fixtures, etc.
done(err, sails);
});
});
after(function(done) {
// here you can clear fixtures, etc.
sails.lower(done);
});

First, you have a typo issue, you have :
var Sails = require('sails');
But you try to use
sails.lower(done);
Instead of
Sails.lower(done);
Then, you need to define a project-wide "migrate" setting configured (http://sailsjs.org/#/documentation/concepts/ORM/model-settings.html?q=migrate)
You just have to edit the config/models.js file.
You can simply uncomment the following line :
//migrate: 'alter'
Enjoy :)

I haven't seen the actual code of your test case, so I just list a couple of possibilities here.
If you run your test asynchronously, I think you need to set the timeout attribute in mocha to some number bigger. I wrote three very simple test cases, but I always get the same problem as you described. I have tried 10000ms, 20000ms and 30000ms. When I increase it to 90000ms, all the test cases are passed. So I think it's because sails really need some time to lift before the test begins.
Another thought is that have you set the 'migrate' attribute in your environment configuration file? If not, sails lift command will wait for your input to select "drop","alter" or "safe" before proceed, which will also cause the timeout problem in test.

Its not like that I know your problem but it might help you find out yours if I write down some snipets of my working tests in sails. I am including the package.json file as well so that you know my version of each module.
here is the relevant modules in package.json:
rootproject/test/mocha.opts , this probably what u need
--timeout 5000
rootproject/package.json
{
...
"dependencies": {
...
"sails": "0.9.7",
"sails-disk": "~0.9.0",
"sails-memory": "^0.9.1",
"sails-mongo": "^0.9.7",
...
},
"devDependencies": {
"mocha": "^1.20.1",
"barrels": "^0.0.3",
"supervisor": "^0.6.0"
},
"scripts": {
"start": "node app.js",
"debug": "node debug app.js",
"test": "PORT=9999 NODE_ENV=test mocha -R spec -b --recursive"
},
"main": "app.js",
...
}
I have also added another model adapter to be used by the tests at rootproject/config/adapters.js
test: {
module : 'sails-memory'
},
rootproject/test/index.js
var assert = require('assert');
var Sails = require('sails');
var barrels = require('barrels');
var fixtures;
var userTest = require('./controllers/User.js');
//... other test controllers ...
//in case you need different simulations per controller you could add a custom Response in your test controller and use them instead
var defaultCustomRequest = function(urlParams, bodyParams/*whatever else u need*/) {
//simulates the sails request
//create an object here, based on how u use the req object in your sails controllers
//.eg
return {
params: urlParams,
body: bodyParams
};
}
//in case you need different simulations per controller or per method you could add multiple custom Responses in your test controller and use them instead
var defaultCustomResponse = function(expectedCode, dataExpecting/*whatever else u need*/) {
//simulates the sails res which I was using like this: res.status(httpCode).json({somedata})
//use the assert function here to validate
//.eg
status: function (responseCode){
assert(expectedCode === responseCode, 'Expected status is ' + expectedCode + ' but ' + responseCode + ' was returned.');
return this;
},
json: function (responseData){
//assert your responseData with your expectedData
return this;
}
return this;
},
}
before(function (done) {
// Lift Sails with test database
Sails.lift({
log: {
level: 'error'
},
adapters: {
default: 'test'
}
}, function(err, sails) {
if (err)
return done(err);
// Load fixtures
barrels.populate(function(err) {
done(err, sails);
});
// Save original objects in `fixtures` variable
fixtures = barrels.objects;
});
});
// Global after hook
after(function (done) {
//console.log('fixtures loaded: ' + JSON.stringify(fixtures));
sails.lower(done);
});
describe('User', function() { userTest.run(fixtures, customRequest, customRespose); });
//or describe('User', function() { userTest.run(fixtures); });
//... other test controllers ...
rootproject/test/controllers/User.js
var assert = require('assert');
var UserController = require('../../api/controllers/UserController.js');
module.exports = {
run: function(fixtures, customReq, customRes) {
describe('create', function(customReq, customRes) {
it ('should create a few user entries', function() {
if (!customReq) customReq = {/*custom request for user create*/};
if (!customRes) customRes = {/*custom response for user create*/};
//call your controllers for testing here
//.eg
UserController.create(
new req({},
{email: 'someemail#gmail.com', password: 'password'})
,new res(201,
{email: 'someemail#gmail.com', password: null});
UserController.create(
new req({},
{email: 'someemail#gmail.com', password: 'password'})
,new res(400,
{error: true});
);
});
});
//... more guns
}
};
As you can see on the package.json I used sails 0.9.7, therefore there might be needed additional changes for 0.10.x versions. .eg instead of config/adapters.js there are the config/models.js and config/connections.js that are used.

The accepted answer is correct for one of the errors.
For the timeout error simply add
this.timeout(5000);
on the line after
before(function(done) {

Related

Loopback testing with supertest, mocha and models

On the Google groups post on deprecating loopback-testing there is a question that asks about providing a proper example of how testing can be achieved without loopback-testing. That thread talks about using supertest instead.
Below is an attempt that I made to combine Mocha, supertest along with models (from app.js). The result works really well when I run the file by itself. But if I had another test file (say test-teacher.js) then the first test file (call it test-student.js) starts to fail in weird ways I can't describe.
Am I missing something or can models not be used like I am using them below?
describe('/Student', function () {
var server = require('../server/server')
var loopback = require('loopback')
var supertest = require('supertest')
var request = require('supertest')(server)
var dataSource = server.dataSource('db', {adapter: 'memory'})
var Student = dataSource.define('Student', {
'id': Number,
'points': Number
});
beforeEach(function () {
Student.updateOrCreate({id: 1, points: 5000});
})
it('Post a new student', function (done) {
request.post('/api/Students').send({points: 5000}).expect(200, done)
})
})
Based on feedback from jakerella on the previous answer, I changed the code above so that I don't have to redefine the models from scratch in the code (thanks jakerella!)
With the code below, I am able to run all the tests from multiple different models as a single suite using npm test without any failures.
Since I am interested in only individual orders ... listen and close were not necessary. I suspect that if I was testing overall instances of models that were created it would become required.
describe('/Student', function () {
var server = require('../server/server')
var request = require('supertest')(server)
var expect = require('expect.js')
var Student
before(function() {
Student = server.models.Student
})
beforeEach(function (done) {
Student.upsert({id: 1, points: 5000}, function() { done() })
})
it('Post a new student', function (done) {
request.post('/api/Students').send({points: 5000}).expect(200, done)
})
})
Wanted to toss this into an answer... the first issue was an undefined dataSource var, but then you also had redefined Student in your two tests. My recommendation instead is used the LoopBack app and models already defined (usually in common/models/).Then the basic implementation for testing (that I use) is something like the code below (using mocha and chai). Note the beforeEach and afterEach to start and stop the server.
var assert = require('chai').assert,
superagent = require('superagent'),
app = require('../server/server');
describe('Person model', function() {
var server;
beforeEach(function(done) {
server = app.listen(done);
});
afterEach(function(done) {
server.close(done);
});
it('should log in and log out with live server', function(done) {
superagent
.post('http://localhost:3000/api/People/login')
.send({ email: 'john#doe.com', password: 'foobar' })
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.end(function(err, loginRes) {
if (err) { return done(err); }
assert.equal(loginRes.status, 200);
assert.ok(loginRes.body);
assert.equal(loginRes.body.userId, 1);
}
});
});
});

provide data for protractor test

I'm new to nodejs and working now to automatize functionnal test doing BDD with cucumber and protractor.
For some of my steps, i need to send query to an oracle database and then use the result to make a search in the tested website.
1- I'm trying with oracleDB witch returns me the expected result that i put in a variable but this one is not available in my steps. Method sendKeys of webdriver puts "undefined" in the input.
2- I'm also wondering if there's not another way because it was hard to install oracledb and the next task is to have jenkins builds
here is my code:
var dbQueryContract = function() {
var oracledb = require('oracledb');
//Database communication
oracledb.getConnection(
{
user : "xxx",
password : "xxx",
connectString : "xxx"
},
function(err, connection)
{
if (err) {
console.error(err.message);
return;
}
connection.execute(
"select xxx, xxx " +
"FROM xxxxx " +
"where xxx is not null and rownum < 5",
{
resultSet: true
},
// bind value for :id [110],
function(err, result)
{
if (err) { console.error(err.message); return; }
console.log(result.rows[0][0]);
});
criteria= result.rows[0][0];
// the connection is ok and i can log result.rows[0][0] i want to use for search
});
};
module.exports = new dbQueryContract();
************************************************************************************************
// Use the external Chai As Promised to deal with resolving promises in
// expectations.
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
var page1 = require('../page1.js');
var page2 = require('../page2.js');
var dbQueryContract = require('../dbQueryContract.js');
var EC = protractor.ExpectedConditions;
// Chai expect().to.exist syntax makes default jshint unhappy.
// jshint expr:true
module.exports = function() {
this.Given(/^thanx for help$/, function(next) {
browser.ignoreSynchronization=true;
browser.get('toto.com');
page1.login.sendKeys('login')
page1.password.sendKeys('P#ssword')
page1.validateButton.click();
browser.ignoreSynchronization=false;
page2.searchLink.click();
browser.waitForAngular();
browser.sleep(5000);
// console.log(dbQueryContract.numabo.result.rows[0][0]);
dbQueryContract().then(function(criteria) {
page2.searchInput.sendKeys(criteria, protractor.Key.ENTER);
});
next();
});
this.When(/^i learn more$/, function(next) {
browser.sleep(5000);
next();
});
};
This code doesn't look right. Your module is exporting the result of invoking new dbQueryContract();. However, that function isn't really a constructor function. You're not setting any properties on this or adding any properties to the prototype. Also, you're not returning anything, which is probably why your getting undefined later.
The next problem is that you're chaining a then call, assuming that the driver uses promises - it doesn't (at least not yet). You'd need to have your function return a deferred which you'd need to resolve at the right time yourself. There's some discussion here about adding more robust JavaScript layer:
https://github.com/oracle/node-oracledb/pull/321
If we go that route I'd like to see all async methods return a promise by default which would make things easier...

Jasmine: how to run series of tests after an event has triggered

I'm trying to run tests on my npm module. The code below will work if I comment out either of the 2 it blocks, but times out if I leave them both in as below. How can wait for "ready" before running my tests (I'd like to add more but they too need to await "ready")?
describe("GA analytics", function() {
var report = new Report(private_key, SERVICE_EMAIL, 1);
it("should connect and emit <ready>", function(done) {
report.on('ready', function() {
console.log("test.js: Token: ", report.token);
expect(report.token).toBeDefined();
done();
});
});
it("should get data correctly", function(done) {
report.on('ready', function() {
report.get(query, function(err, data) {
if (err) throw err
expect(data.rows).toEqual([ [ '5140' ] ]);
done();
});
});
});
});
I guess it is happening because you create a new instance of the Report only once per test file, therefore the ready event only fires once and only the first it block in the test will catch it and process. The rest of the it blocks won't receive any more ready events, so they silently wait for it until Jasmine times out. The solution will be to create a new Report instance before each it block, which can be easily done with the help of Jasmine beforeEach:
describe("GA analytics", function() {
var report;
beforeEach(function () {
report = new Report();
});
// ....
});
See the working example here on Plunker (open a "script.js" file)

Object state leaking between Mocha tests

I'm new to Mocha and I'm hoping someone can help me. I'm curious how I can ensure that actions performed in one test do not effect another. For example, assume that I have a function microbe that returns an object that looks like:
{
_state : {
views : 'views'
}
set : function(key, val) { this._state[key] = val }
}
This is a majorly reduced version of what I'm working with, but it demonstrates what I'm testing, using a test suite such as:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('../microbe/index'), app;
beforeEach(function(done) {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
The second test fails because the first test sets app._state.views to 'test' so it overrides the default of 'views'. I thought Mocha would isolate each test, but apparently not. I've tried to just re-instantiate the app for each test as shown usingbeforeEach, but it still seems to be leaky.
Whats the best way to do this? I know I could just reverse the order of the tests and it would technically pass, but I'd rather fix the issue itself than just avoid it
It looks as if your function microbe() is behaving as a singleton.
The easiest way of having a fresh instance of your microbe is to use the new keyword within the beforeEach():
beforeEach(function(done) {
app = new microbe();
});
Doing this, each execution of a test will start freshly with a 'default' instance of the object you are returning.
I just tested the code you posted and the tests run correctly:
test.js:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('./microbe.js'), app;
beforeEach(function() {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
microbe.js:
module.exports = function () {
return {
_state : {
views : 'views'
},
set : function(key, val) { this._state[key] = val; }
};
};
Executing mocha outputs:
/tmp mocha -R spec test.js
State
✓ should update the view location with app.set
✓ should return "views" as the defualt view location
2 passing (5ms)
You have something strange in your implementation (maybe a singleton or a global variable somewhere) but it's not Mocha's fault.

How to integratei sequelize in nodeunit test

I have an express-sequelize application, it uses nodeunit to test routes and for this I use the wonderfull nodeunit-express module.
All works fine, except sequelize creating the database. In my express app I do a sequelize.sync({force: true}) during the start.
My tests do work when I run
npm start (node server.js)
first, followed by
npm test (nodeunit test)
But when the database is not present and I run
npm test (nodeunit test)
Alone, the is not created errors.
Where in the nodeunit testing process should I place the sync sequelize to have it available for my tests?
var request = require('nodeunit-express');
var server = require('../server');
var models = require('../models');
var express = request(server.app);
exports["Rest interfaces"] = {
setUp: function (callback) {
//insert some stuff in the database?
callback();
},
tearDown: function (callback) {
callback();
//cleanup();
},
"/api/categories": function (test) {
express.get('/api/categories').expect(function (response) {
// response is the response from hitting '/'
var result = JSON.parse(response.body);
test.equal(response.statusCode, 200, '/api/categories is not available');
test.equal(result.length, 0, 'no categories should be returned');
test.done();
});
}
};
function cleanup() {
//wait a little
setTimeout(function () {
express.close();
}, 500);
}

Resources