I am using mocha for tests on a new project.
For a test file containing dummy express server all works fine. Here is the code:
describe('Our application', function() {
let app, server;
before(function(done){
app = createApp();
server = app.listen(function(err) {
if (err) { return done(err); }
done();
});
})
after(() => {
server.close()
})
it('should execute fail', function(done) {
request(app)
.get('/')
.set('Content-Type', 'application/json')
.expect(200, (err, res) => {
if(err) return done(err);
expect(res.body.success).to.equal('abcd');
done();
});
});
it('should execute fail', function(done) {
request(app)
.get('/')
.set('Content-Type', 'application/json')
.expect(500, (err, res) => {
if(err) return done(err);
expect(res.body.success).to.equal(true);
done();
});
});
it('should execute basic root request', function(done) {
request(app)
.post('/')
.set('Content-Type', 'application/json')
.send({a: 1})
.expect(200, (err, res) => {
if(err) return done(err);
expect(res.body.success).to.equal(true);
done();
});
});
});
I deliberately made all tests fail, but each is executed and I see the result:
Our application
1) should execute fail
2) should execute fail
3) should execute basic root request
0 passing (19ms)
3 failing
1) Our application
should execute fail:
Error: expected 200 "OK", got 400 "Bad Request"
...
However, when I run it for our server (use different logic in before and after hook to start/close server), the tests exit after first failure and show a different assertion error:
1) should fail when registering with email without top level domain
AssertionError: expected 'Invalid user information provided, or…' to deeply equal 'Invalid email provided'
at Test.<anonymous> (file:///Users/azildzic/scaleup/postmarket-backend/test/api/authentication/registration-spec.js:68:34)
at Test.assert (node_modules/supertest/lib/test.js:172:8)
at Server.localAssert (node_modules/supertest/lib/test.js:120:14)
at Object.onceWrapper (node:events:627:28)
at Server.emit (node:events:513:28)
at emitCloseNT (node:net:1880:8)
at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
showDiff: true,
actual: 'Invalid user information provided, or not every field provided',
expected: 'Invalid email provided',
operator: 'strictEqual',
uncaught: true
}
So in the first case I got Error, in the second I got AssertionError, why?
Also, in the first case there is no this part at the end of stack trace:
showDiff: true,
actual: 'Invalid user information provided, or not every field provided',
expected: 'Invalid email provided',
operator: 'strictEqual',
uncaught: true
Why?
It's worth mentioning that both test files are using mocha/chai/supertest. Also, the second test files has async actions defined in controller. Is this maybe a circuit breaker?
Any help is appreciated, as I'm quite new to mocha/chai and node.js in general
Related
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!
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 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 am trying to write tests to ensure that my Express API is correctly returning the right HTTP status codes for various scenarios. I am using Mocha, and Supertest for requests to the API in the tests. Right now I'm getting very unexpected results, which are detailed below.
Using: Express, body-parser, Sequelize, Mocha, Supertest
GET /users/:id
models.User.find(req.params.id).complete(function(err, foundUser) {
if (err) {
logger.error(err, 'Error');
return err;
}
console.log('user: ' + foundUser);
if (foundUser != null) {
res.json({ user: foundUser.getJsonRepresentation() });
}
else {
res.status(404);
res.json({ error: 'Not found' });
}
});
Tests for this method
it('responds with the right user', function(done){
request(app)
.get(apiPath + '/users/' + createdUser.id)
.set('Accept', 'application/json')
.expect(function(res) {
res.body.user.id.should.equal(createdUser.id);
})
.expect(200, done);
});
it('responds with the right error code for non-existent resource', function(done) {
request(app)
.get(apiPath + '/users/1000')
.expect(404, function(err, res) {
console.log(err);
console.log('Response: ' + res);
done();
});
});
For the 404 test, I get this error: { [Error: Parse Error] bytesParsed: 12, code: 'HPE_INVALID_STATUS' } in the callback. The res is undefined.
I have tried several different syntax forms for this expect call: .expect(404, function(err, res) { and none of them have worked. I've also tried all the different syntax forms for this as well:
res.status(404);
res.json({ error: 'Not found' });
Can anyone offer some insight into what is going on here?
Maybe the issue is in the following code snippet:
if (err) {
logger.error(err, 'Error');
return err;
}
It seems to be triggering the error but not actually returning it to the supertest client.
You can try to return the error to the client instead of only logging it, like this:
if (err) {
logger.error(err, 'Error');
res
.status(500);
.json({ error: error.message });
return err;
}