I have set up an Express API with a couple of tests inside a Windows OS, the first test ran successfully inside of wrongo/test/app_test.js:
const assert = require('assert');
const request = require('supertest');
const app = require('../app');
describe('the express app', () => {
it('handles a GET request to /api', done => {
request(app).get('/api').end((err, response) => {
assert(response.body.how === 'dee');
done();
});
});
});
I later developed another test at wrongo/test/controllers/users_controller_test.js:
const assert = require('assert');
const request = require('supertest');
const app = require('../../app');
describe('Users controller', () => {
it('Post to /api/users creates a new user', done => {
request(app).post('/api/users').send({ email: 'test#test.com' }).end(() => {
done();
});
});
});
It does not run this second test at all and I don't get my console log from users_controller.js file:
module.exports = {
greeting(req, res) {
res.send({ how: 'dee' });
},
create(req, res) {
console.log(req.body);
res.send({ how: 'dee' });
}
};
I don't believe I have any syntax errors, not sure why Mocha will not just run this second test.
It seems Mocha cannot find a test if nest it within a folder within a test/ folder, but when I moved the test file to the main test/ folder, Mocha was able to run the test. The reason being that if you look at your "scripts" object inside of package.json there is a --recursive missing within:
"test": "nodemon --exec mocha -R min"
But I had to write the test script that way because Windows 10 kept giving me an error when I had --recursive.
Related
I'm setting unit test for my nodejs app. To test the setup I have a very simple express route which looks as below
todo.ts
import { Router } from "express";
const router = Router();
router.get("/todo", async (req, res) => {
return res.status(200);
});
export default router;
Then I have created a folder _tests with a subfolder unit
todo.test.ts
/*
* #group unit
*/
import request, { SuperAgentTest } from "supertest";
import app from "../../../server";
let router: SuperAgentTest;
beforeAll(async () => {
router = request.agent(app);
});
afterAll(async () => {
await dbClose();
});
describe("ToDo API", () => {
// Test Case -> 200
it("Should return 200", async () => {
const result = await router.get("/todo");
expect(result.status).toEqual(200);
});
});
I have the script in my package.json to run the unit tests is
"test:unit": "jest --verbose --detectOpenHandles --group=unit",
Now when I'm running the test from cmd/bash npm run test:unit I keep getting the below error
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● TCPSERVERWRAP
What's is causing this & how to get rid of it?
Thanks!
I have got a local development environment working for Node/PostgreSQL/Knex in the sense that I can post to a development database on my machine using an API. I am now trying to create tests for this functionality but am getting an error.
Here's my config:
//knexfile.js
module.exports = {
development: {
client: 'pg',
connection: {
host: '127.0.0.1',
user: 'dbUser',
password: 'dbpword',
port: 5432,
database: 'example-name'
},
migrations: {
directory: __dirname + '/db/migrations'
},
seeds: {
directory: __dirname + '/db/seeds/development'
}
},
}
//db.js
const config = require('../knexfile.js');
const env = process.env.NODE_ENV || 'development';
const knex = require("knex")(config[env]);
module.exports = knex;
knex.migrate.latest([config]);
And then my tests:
import chai from 'chai';
import { expect } from 'chai';
import chaiHttp from 'chai-http';
import knex from '../../db/db';
import app from '../../server';
chai.use(chaiHttp);
describe('Tokens API', () => {
beforeEach((done) => {
knex.migrate.rollback()
.then(() => {
knex.migrate.latest()
.then(() => {
return knex.seed.run()
.then(() => {
done();
});
});
});
});
afterEach((done) => {
knex.migrate.rollback()
.then(() => {
done();
});
});
describe('POST /users', () => {
it('posts a list of users to the database with all mandatory fields', (done) => {
chai.request(app)
.post('/users')
.send({
"users": [
"steve",
"whoever",
"matt",
"another"]})
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res).to.be.json;
done();
});
});
});
});
When I run this I get the following error twice - I think for the knex calls in the beforeEach block:
Knex:warning - Can't take lock to run migrations: Migration table is already locked
Knex:warning - If you are sure migrations are not running you can release the lock manually by deleting all the rows from migrations lock table: knex_migrations_lock
Unhandled rejection MigrationLocked: Migration table is already locked
I have tried numerous things - including clearing out the knex_migrations_lock table. The only support I can find online is this thread, which suggests clearing out the lock table using DELETE FROM Migrations_lock where id <> 0;, however my lock table only has a is_locked column with zero values.
Any idea what's going on?
EDIT: I've just realised if you edit out all the knex calls, the test actually passes. Could this be because I am effectively calling knex twice - once from db.js and once indirectly through server.js? If that's the case, how do I avoid doing this - because surely I need to call the knex setup for Node to run it?
For anyone stumbling across this, the problem was actually coming from db.js, specifically the last line:
const config = require('../knexfile.js');
const env = process.env.NODE_ENV || 'development';
const knex = require("knex")(config[env]);
module.exports = knex;
knex.migrate.latest([config]);
Of course this is asynchronous, and the tests were importing this file before trying to run their own knex functions, causing the lock. I got round this by adding a clause to block this running while testing:
if(process.env.NODE_ENV != 'test') {
knex.migrate.latest([config])
}
You can then create a test environment by adding process.env.NODE_ENV='test' to each spec file, or by installing the npm env test module.
Had the exact same issue, ended up being due to my API calling the database when being initialized by the supertest library.
For example, my test file:
var db = require('../db');
var api = require('../api');
var supertest = require('supertest')(api);
describe('Session routes', () => {
beforeEach((done) => {
db.migrate.rollback()
.then(() => {
db.migrate.latest()
.then(() => {
return db.seed.run()
.then(() => {
done();
});
});
});
});
afterEach((done) => {
db.migrate.rollback()
.then(() => {
done();
});
});
it('GET /session should error with no token', (done) => {
supertest
.get('/session')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(401, {
error: 'Unauthorized'
}, done);
});
});
On line 2, it requires my api - when my api is required the following code gets run straight away to initialize my api's external services API:
var db = require('./other-postgres-library');
var servicesApi = require('./services/api')(db);
This would connect to a bunch of external services and write the results to the database.
So when tests were running my APP was throwing errors because it was trying to write to a database which was being rolled back/migrated/seeded etc.
I changed my inner services API to initialize lazily and all my problems have disappeared.
In your case, I would hazard to guess when your tests runs this line
import app from '../../server'; your app/server code is trying to running some queries against the database.
I have two test files. When only test1.js is present, no tests run, and mocha reports "0 passing" tests. When test1.js and test2.js are present but both depend on a promise, then still no tests run and mocha still reports "0 passing". But when one of the tests is modified to not use a promise, mocha runs both tests and they succeed. What the heck? Here are my files:
index.js:
require('./server').then( function(server) {
server.listen(8080, function() {
console.log("Started server");
});
);
server.js:
var express = require('express');
var server = express();
module.exports = new Promise((function(resolve, reject) {
return resolve(server);
}));
test1.spec.js:
require('./server').then(function(server) {
describe('Test Suite #1', function () {
it('should run test #1', function testSomething(done) {
return done();
});
});
});
test2.spec.js (server.js used as promise, tests do not run):
require('./server').then(function(server) {
describe('Test Suite #2', function () {
it('should run test #2', function testSomethingElse(done) {
return done();
});
});
});
test2.spec.js (server.js not used as promise, both tests run):
var server = require('./server');
describe('Test Suite #2', function () {
it('should run test #2', function testSomethingElse(done) {
return done();
});
});
To run them, I just have nodejs, express, and mocha installed and run:
% mocha "*.spec.*"
I understand I'm not using the server variable in these examples, but of course the real tests need to return a promise because sometimes the server.js is accessing remote systems for config data. I could work around this, but any help in understanding what's happening here would be greatly appreciated!
You need to describe and define all your tests synchronous. Otherwise they aren't recognized by mocha. If you have some async setup to make, use the before or beforeEach functions:
describe('Test Suite #1', function () {
var server;
before(function(done){
require('./server').then(aServer => {
server = aServer;
done();
});
});
it('should run test #1', function testSomething(done) {
return done();
});
});
I am starting my node server in my before block on my mocha chai-http tests.
I have it working perfect for single test files. However when I attempt to run multiple tests in a single command NODE_ENV=test mocha test/**/*.js I am getting an error.
I tried to have the node servers launch on different ports per test file. This didn't work, got node server start errors.
I'm now thinking it would be great if I can have a single mocha file that runs before my other test files to start the server and then a single file that runs after the other test files to kill/stop the server.
How would I go about this.
Below is some of my code:
Here is one of my test files for reference:
var chai = require('chai');
var chaiHttp = require('chai-http');
chai.use(chaiHttp);
var expect = chai.expect;
var Sails = require('sails');
describe('REST User API', function() {
var app; // for access to the http app
var sails; // for starting and stopping the sails server
before(function (done) {
Sails.lift({
port: 3001,
log: {
level: 'error'
}
}, function (_err, _sails) {
if(_err){
console.log("Error!", _err);
done();
}
else {
app = _sails.hooks.http.app;
sails = _sails;
done();
}
});
});
describe("user session", function () {
var res; // http response
var authenticatedUser;
before(function (done) {
chai.request(app)
.post('/users/signin')
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.send({ email: 'admin#test.com', password: 'secret'})
.end(function (_res) {
res = _res; // Record the response for the tests.
authenticatedUser = JSON.parse(_res.text); // Save the response user for authenticated tests
done();
});
});
it("should connect with a 200 status", function () {
expect(res).to.have.status(200);
});
it("should have a complete user session", function () {
var userSession = authenticatedUser;
expect(userSession).to.have.property('firstName');
expect(userSession).to.have.property('lastName');
expect(userSession).to.have.property('gender');
expect(userSession).to.have.property('locale');
expect(userSession).to.have.property('timezone');
expect(userSession).to.have.property('picture');
expect(userSession).to.have.property('phone');
expect(userSession).to.have.property('email');
expect(userSession).to.have.property('username');
expect(userSession).to.have.property('confirmed');
expect(userSession).to.have.property('status');
expect(userSession).to.have.property('authToken');
});
});
after(function (done) {
sails.lower(function() {
done()
});
});
});
From mocha v8.2.0, you can use GLOBAL FIXTURES to setup and teardown your web server for all test suites. Global fixtures are guaranteed to execute once and only once.
I have a nodejs restful style service which has no front end, it just accepts data and then does something with it.
I have unit tested most of the method level stuff I want to, however now I want to basically do some automated tests to prove it all works together. When I am using ASP.MVC and IIS its easy as the server is always on, so I just setup the scenario (insert dummy guff into DB) then make a HttpRequest and send it to the server and assert that I get back what I expect.
However there are a few challenges in nodejs as the applications need to be run via command line or some other mechanism, so given that I have an app.js which will start listening, is there some way for me to automatically start that going before I run my tests and then close it once my tests are finished?
I am currently using Yadda with Mocha for my testing so I can keep it written in a BDD style way, however I am hoping the starting of the web app is agnostic of the frameworks I am using.
Just expose some methods to start and stop your webserver. Your app.js file could be something like this:
var app = express()
var server = null
var port = 3000
// configure your app here...
exports.start = function(cb) {
server = http.createServer(app).listen(port, function () {
console.log('Express server listening on port ' + port)
cb && cb()
})
}
exports.close = function(cb) {
if (server) server.close(cb)
}
// when app.js is launched directly
if (module.id === require.main.id) {
exports.start()
}
And then in your tests you can do something like this (mocha based example):
var app = require('../app')
before(function(done) {
app.start(done)
})
after(function(done) {
app.close(done)
})
Have a look to supertest https://github.com/visionmedia/supertest
You can write test like
describe('GET /users', function(){
it('respond with json', function(done){
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
})
})
Using gimenete's answer, here's an example of a service (server) with async await and express:
service.js:
const app = require('express')()
const config = require('./config')
let runningService
async function start() {
return new Promise((resolve, reject) => {
runningService = app.listen(config.get('port'), config.get('hostname'), () => {
console.log(`API Gateway service running at http://${config.get('hostname')}:${config.get('port')}/`)
resolve()
})
})
}
async function close() {
return new Promise((resolve, reject) => {
if (runningService) {
runningService.close(() => {
})
resolve()
}
reject()
})
}
module.exports = {
start,
close
}
service.spec.js:
const service = require('../service')
beforeEach(async () => {
await service.start()
})
afterEach(async () => {
await service.close()
})