The problem is as weird as the title. I have a project which I created using the generator angular-fullstack, which I am connecting to a MSSQL server using Sequelize (who the f uses MSSQL... client's demands) and everything has been working really well until I had to create the 8th endpoint (using angular-fullstack:endpoint).
Every time I created an endpoint all the test (automatically created and executed using mocha) would work except for the PATCH verb integration test, which I would just eliminate as I am not using PATCH at all.
After I created the 8th endpoint (doing the same I did for every other one) the integration tests created by the generator itself (the unit tests work perfectly) started to fail (not just the endpoint's test, but other tests that used to work before), and they fail randomly (sometimes 3 of them fail, sometimes 4, and sometimes they all work), which makes me think of some kind of race condition (which I wasn't able to find).
Findings:
The POST integration test "works" but the result doesn't show up in the database. The log shows a correct SQL command sent to the database:
INSERT INTO [Findings] ([name],[info],[createdAt],[updatedAt]) OUTPUT INSERTED.* VALUES (N'New Finding',N'This is the brand new finding!!!',N'2018-03-05 22:30:24.000',N'2018-03-05 22:30:24.000');, and it returns 201 as status.
When the status code returned is 500, the error is usually name: 'SequelizeDatabaseError',
message: 'Invalid object name \'Findings\'.', as if the Table didn't exist, but it does!
If you have any idea on what can I try, I will be more than grateful! (I have already searched everywhere I could think of, but it's even hard to search for this problem)
This is the file containing the last-endpoint-created's tests. I can add any other file that might help to find a possible solution!
'use strict';
/* globals describe, expect, it, beforeEach, afterEach */
var app = require('../..');
import request from 'supertest';
var newFinding;
describe('Finding API:', function() {
describe('GET /api/findings', function() {
var findings;
beforeEach(function(done) {
request(app)
.get('/api/findings')
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
findings = res.body;
done();
});
});
it('should respond with JSON array', function() {
expect(findings).to.be.instanceOf(Array);
});
});
describe('POST /api/findings', function() {
beforeEach(function(done) {
request(app)
.post('/api/findings')
.send({
name: 'New Finding',
info: 'This is the brand new finding!!!'
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
newFinding = res.body;
done();
});
});
it('should respond with the newly created finding', function() {
expect(newFinding.name).to.equal('New Finding');
expect(newFinding.info).to.equal('This is the brand new finding!!!');
});
});
describe('GET /api/findings/:id', function() {
var finding;
beforeEach(function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
finding = res.body;
done();
});
});
afterEach(function() {
finding = {};
});
it('should respond with the requested finding', function() {
expect(finding.name).to.equal('New Finding');
expect(finding.info).to.equal('This is the brand new finding!!!');
});
});
describe('PUT /api/findings/:id', function() {
var updatedFinding;
beforeEach(function(done) {
request(app)
.put(`/api/findings/${newFinding._id}`)
.send({
name: 'Updated Finding',
info: 'This is the updated finding!!!'
})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if(err) {
return done(err);
}
updatedFinding = res.body[0];
done();
});
});
afterEach(function() {
updatedFinding = {};
});
it('should respond with the updated finding', function() {
expect(updatedFinding).to.equal(1);
});
it('should respond with the updated finding on a subsequent GET', function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
let finding = res.body;
expect(finding.name).to.equal('Updated Finding');
expect(finding.info).to.equal('This is the updated finding!!!');
done();
});
});
});
});
I found a workaround and I think I have found the problem, but I don't understand why it is happening now, and not before.
Some tests were being executed before the db connection was established, so I have added
before(function(done) {
app.on('appStarted', function() {
done();
});
});
to the new test file, and now it works without any problem!
Related
I am using supertest, mocha and expect for testing my app. I encountered an issue where the document returned is null and there is no error.
router.get('/user', function (req, res) {
User.findOne({
_id: '56c59bb07a42e02d11a969ae'
}, function (err, user) {
if(err) return res.status(404).json({message: 'not found: ' + err.message});
res.status(200).json(user);
});
});
When I test this on Postman I always get 200 which is what I expected but when I run the test I get 404 :(
My simple test code below where I always get the 404.
it('get user', function (done) {
request(app)
.get('/user')
.expect(200)
.end(function (err, res) {
if (err) throw err;
done();
});
});
Both Postman and the test are referring to the same mongoose database so I'm sure that it should be able to fetch the user. How mongoose and the app are setup in my server below.
mongoose.connect('mongodb://localhost/scratch', options);
app.listen(port, function () {
console.log('Scratch started on port ' + port);
});
Is there something I need to do to make it work?
I modified the test a bit where the User is created on 'before'.
before(function (done) {
connection.on('error', console.error);
connection.once('open', function () {
done();
});
mongoose.connect(config.db[process.env.NODE_ENV]);
var userInfo = {
"username": "naz2#gmail.com",
"password" : "123456",
"nickname": "naz"
}
var newUser = User(userInfo);
newUser.save(function (err, doc) {
if(err) {
console.log('err: ' + err.message);
} else{
console.log('saved');
}
})
console.log(mongoose.connection.readyState);
done();
});
Then ran the same test and it worked!
My guess is that during the test the app is querying against documents in memory( I verified that by checking the db and the new user was not added) and not to an existing document like I was expecting when testing with Postman. Which means I need to seed the test db first before I can use it for the test.
I am new to Nodejs and I'm curious to what caused the documents to be created in memory and how mongoose/express know that it is ran by a test/supertest and behave accordingly.
I'm writing some tests for an express app and I am wondering how to properly access a variable in one assertion block from another. The variable I am trying to access is this.token = res.body.token
Whenever I try to access it, it comes up undefined (other than when accessing it within the beforeEach block). How can I access this variable? I need to use the token to set the headers in my test for my POST request.
Code:
describe('CRUD: tests the GET & POST routes', () => {
beforeEach(done => {
chai.request('localhost:3000')
.post('/app/signup')
.send({ email: 'meow#test.com', password: 'testpass' })
.end((err, res) => {
if (err) return console.log(err);
this.token = res.body.token; // this variable holds a token when accessed within this scope (tested it with node debugger)
done();
});
});
it('should create with a new cat with a POST request', (done) => {
chai.request('localhost:3000')
.post('/app/cats')
.set('token', this.token) // when accessed here, it is undefined...
.send({ username: 'cat_user' })
.end((err, res) => {
expect(err).to.eql(null);
expect(res).to.have.status(200);
expect(res.body.name).to.eql('test cat');
expect(res.body).to.have.property('_id');
done();
});
});
EDIT: Here is a screenshot of my terminal in node debug mode. As you can see, when it hits the first debugger break and _token is accessed, it contains the token. In the next debugger break, however, it comes up empty... (maybe that means something else in the debugger?)
You can move your variable to the scope of your describe.
describe('CRUD: tests the GET & POST routes', () => {
let _token;
beforeEach(done => {
chai.request('localhost:3000')
.post('/app/signup')
.send({ email: 'meow#test.com', password: 'testpass' })
.end((err, res) => {
if (err) return console.log(err);
_token = res.body.token; // this variable holds a token when accessed within this scope (tested it with node debugger)
done();
});
});
it('should create with a new cat with a POST request', (done) => {
chai.request('localhost:3000')
.post('/app/cats')
.set('token', _token) // when accessed here, it is undefined...
.send({ username: 'cat_user' })
.end((err, res) => {
expect(err).to.eql(null);
expect(res).to.have.status(200);
expect(res.body.name).to.eql('test cat');
expect(res.body).to.have.property('_id');
done();
});
});
You should read this to understand this: http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/
I am trying to test a route with authentication in my Node / Express / Mongoose back-end.
Here's the test file
var should = require('should');
var _ = require('lodash');
var async = require('async');
var app = require('../../../../app');
var request = require('supertest');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Firm = mongoose.model('Firm');
var firm, user, userPassword, createdFirm, loggedInUser;
describe('GET /api/firms', function(){
beforeEach(function (done) {
firm = new Firm({
company: 'My test company',
corporateMail: 'test.com'
});
userPassword = 'password';
user = new User({
fistname: 'Fake User',
lastname: 'Fake User',
email: 'test#test.com',
job: 'Partner',
firmName:firm.company,
password:userPassword,
isActivated:true,
_firmId:firm._id
});
function createFirm(cb){
request(app)
.post('/api/firms')
.send(firm)
.expect(201)
.end(function(err, res){
if ( err ) throw err;
createdFirm = res.body;
cb();
});
}
function createUser(cb){
request(app)
.post('/api/common/users')
.send(user)
.expect(200)
.end(function(err, res){
createdUser = res.body;
if ( err ) throw err;
cb();
});
};
async.series([function(cb){
createFirm(cb);
}, function(cb){
createUser(cb);
}], done);
});
afterEach(function (done) {
firm.remove();
user.remove();
done();
});
it('should respond with 401 error', function(done) {
request(app)
.get('/api/firms')
.expect(401)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should login', function(done) {
request(app)
.post('/auth/local')
.send({email:user.email, password:user.password})
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should respond with 200 after login', function(done) {
request(app)
.get('/api/firms')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
In the workflow the firm object is created first and then returns its Id so I can create the user with the firmId as a reference.
I would like to test the /api/firms route after the user is authenticated but in spite of my various attempts (using superagent, logging in the before section) I always get a 401 response in the last should section instead of an expected 200.
Actually the important thing to keep in mind is, as KJ3 said, how the authentication is set up. In my case I forgot to mention that I was using jwt. The way it works is the following, you supply a username and a password and the server returns a token created with jwt.
So it makes sense to send the token back for each request in the tests.
The way to achieve this is first by storing the token after authentication in the before section
function createUser(cb){
request(app)
.post('/api/users')
.send(user)
.expect(200)
.end(function(err, res){
if ( err ) throw err;
authToken = res.body.token;
cb();
});
};
Then by adding .set in the request with the token in the correct format ('Bearer ' + token , which is defined in the authentication service):
it('should respond with 200', function(done) {
var authToken = 'Bearer ' + createdUser.token;
request(app)
.get('/api/firms')
.set('Authorization', authToken)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
In the case the test sends a 200 back, which is expected and sends a 401 if the .set(...) is commented out.
Good news is that this is achieved with supertest, so no need to add anything, less good news is that you need to add the .set(...) to each test request.
If you were to go through the last 2 tests in a browser, depending on how you have it setup, yes it would work thanks to cookies and sessions, but here the /api/firms test is independent of the auth/local test. So a 401 is the correct response.
It really depends on how your auth is setup, but you need to authenticate on the /api/firms test too. Either by sending the credentials again (every single one of my mocha tests authenticates each time) or implement sessions into the tests, see this SO post for some direction.
I write test for my sails application, but have some problem in controller. When user signup I check for duplication, if yes - render form with error, if no - render form with success message. So can anyone know how to test it right? Or maybe you can suggest better code structure, thanks a lot.
Controller :
User.findOne({ email: req.body.email }, function(err, user) {
if(err) return res.json(err)
if(!user) {
User.create({email:req.body.email, password:req.body.password}).exec(function createCB(err, user){
if(err) return res.json(err);
res.view('signup', { message: 'Done, user created'})
})
} else
res.view('signup', { message: 'User already exist'})
})
Test :
it('should show error if duplicated user - TODO', function (done) {
request(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(done)
})
So question is, how can I test res.view?
I usually use simple request at my test if I want to test my view. Like this:
it('able to test view', function(done) {
require('request-promise')('http://localhost:1337/')
.then(function(res) {
res.should.contains('Done, user created');
done();
})
.catch(done);
});
It will check the whole body, is there any match string that we provide. Remember to do npm install request-promise --save-dev first (or any relevant library that can make request).
Or you can use supertest library as mentioned by #Yann like this
it('should be able to test view', function(done) {
require('supertest')
.agent(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.should.contains('Done, user created');
return done();
});
});
You can use a library like supertest to do an HTTP request:
it('should be able to test view', function(done) {
require('supertest')
.agent(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.should.contains('Done, user created');
return done();
});
});
I want to write some tests for some routes and I want to do something like this:
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('Create and check that the create was successfull', function() {
var user_id = '';
it('should add a new case and return a JSON array', function(done) {
var newUser = {nume : 'Test', prenume: 'test', varsta : 23};
request(app)
.post('/api/new_user')
.send(newUser)
.expect(201)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Array);
res.body.should.have.property('_id');
user_id = res.body._id;
done();
});
});
it('should return the new user ', function(done) {
request(app)
.get('/api/new_user/' + user_id)
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Object);
res.body._id.should.be.exactly(user_id);
done();
});
});
});
I am not sure if the two it statements are executed one after the other or each one is done async and when I get to the second it the first one is not executed so it will fail because the insert was not done in the server.
Should I use async.series?
In your example supertest is only responsible for the chain from request(app) down, so it's actually the provider of the describe() and it() calls that determines the order, or lack thereof, in which your tests are executed, which I guess is mocha, right?
If so, Mocha will run your testcases in order (as in, the second one will be called once the first one has finished).
Supertest request can't persist session therefore your second test case seems failing. Basically the second case running as it is not aware at all about the first case.
You can persist your session with request.agent.
Below is a quick example:
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('Create and check that the create was successfull', function() {
var session;
var new_user = {name:'Test',presume:'test',vast:'23'};
var user_id;
before (function(done){
session = request.agent(app);
session
.post('/api/new_user')
.send('new_user')
.expect(201)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Array);
res.body.should.have.property('_id');
user_id = res.body._id;
done();
});
});
it('should return the new user ', function(done){
session
.get('/api/new_user/' + user_id)
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Object);
res.body._id.should.be.exactly(user_id);
done();
});
});
...
});