Ensuring Express App is running before each Mocha Test

后端 未结 6 2069
别跟我提以往
别跟我提以往 2020-12-13 15:00

I am working on developing a REST API using ExpressJS, NodeJS, Mongoose and Mocha.

The thing is that I have an app.coffee file, thats responsible for setting up Expr

相关标签:
6条回答
  • 2020-12-13 15:24

    I am late to the party, but I found the best way to set up my mocha test suite for an express app is to make my app.js or server.js file export the app object, like this:

    var mongoose = require('mongoose');
    var express = require('express');
    require('express-mongoose');
    
    var env = process.env.NODE_ENV || 'development';
    var config = require('./config/config')[env];
    
    var models = require('./app/models');
    var middleware = require('./app/middleware');
    var routes = require('./app/routes');
    
    var app = express();
    
    app.set('port', process.env.PORT || config.port || 3000);
    app.set('views', __dirname + '/app/views');
    app.set('view engine', 'jade');
    
    // database
    mongoose.connect(config.db);
    
    // middleware
    middleware(app);
    
    // Application routes
    routes(app);
    
    app.listen(app.get('port'));
    console.log('Express server listening on port ' + app.get('port'));
    
    // export app so we can test it
    exports = module.exports = app;
    

    make sure your config file has different environments like development, test, production set up:

    var path = require('path');
    var rootPath = path.normalize(__dirname + '/..');
    
    module.exports = {
      development: {
        db: 'mongodb://localhost/my_dev_db',
        port: 3000
      },
      test: {
        db: 'mongodb://localhost/my_test_db',
        port: 8888
      },
      production: {
        // ...
      }
    }
    

    then in your test files you can go ahead and require your app, which will connect to the right db and on the right port:

    var should = require('chai').should();
    var request = require('supertest');
    var mongoose = require('mongoose');
    
    var app = require('../app');
    var agent = request.agent(app);
    
    var User = mongoose.model('User');
    
        // get users
        describe('GET /api/users', function() {
          it('returns users as JSON', function(done) {
            agent
            .get('/api/users')
            .expect(200)
            .expect('Content-Type', /json/)
            .end(function(err, res) {
              if (err) return done(err);
              res.body.should.have.property('users').and.be.instanceof(Array);
              done();
            });
          });
        });
    

    And finally, to start up the whole monster you include this in your package.json (make sure to have nodemon and mocha in your devDependencies):

    "scripts": {
        "start": "NODE_ENV=development ./node_modules/.bin/nodemon app.js",
        "test": "NODE_ENV=test ./node_modules/.bin/mocha --reporter spec test/**.js"
      }
    

    Now you can start your test suite with npm test and your app with npm start.

    Hope it helps! ps: most of the stuff I learned from looking at this amazing example: https://github.com/madhums/node-express-mongoose-demo

    0 讨论(0)
  • 2020-12-13 15:29

    You don't need to listen a port for testing your app. You can use supertest testing library with your app and it should be ok.

    You would probably need to connect to a database or redis client though. You can do this in configure method of your app. Since node caches modules, you can require app module in different test modules without reconnecting.

    0 讨论(0)
  • 2020-12-13 15:39

    I ran into the same issue while using jasmine/supertest to test my express app. I solved the issue by emitting when the app was ready and only running my tests afterwards. Here is my directory structure

    - server.js
    - spec
        - setup.spec.js
        - test.spec.js
    

    In server.js when ever your server is set up and you are ready to run your tests add app.emit('started');. And make sure to export your app! In setup.spec.js you can watch for the event to be emitted.

    server.js

    const express = require('express');
    const app = express();
    module.exports = app; // for testing
    
    setTimeout(() => {
        app.listen(process.env.PORT || 3000);
    });
    

    setup.spec.js

    const server = require('../index');
    
    beforeAll(done => server.on('started', done));
    

    test.spec.js

    const request = require('supertest');
    const server = require('../index');
    
    describe('test', () => {
        it('test server works', done => {
            request(server).get('/test').expect(200);
         });
    });
    

    The same idea should work for mocha as well. Here is an article explaining this for mocha. You should just change beforeAll to before.

    0 讨论(0)
  • 2020-12-13 15:41

    There might be a more straightforward way, but I went to Grunt for automating my functional tests. There's an express and mocha plugin to reach your goal. My gruntfile:

    'use strict';
    
    module.exports = function (grunt) {
    grunt.initConfig({
        express: {
            options: {}
          , test: {
                options: {
                    script: './app.js'
                }
            }
        }
      , simplemocha: {
            options: {
                globals: ['should']
              , timeout: 8000
              , ignoreLeaks: false
              , ui: 'bdd'
              , reporter: 'tap'
            }
          , all: { src: ['tests/*.test.js'] }
        }
    })
    
    grunt.loadNpmTasks('grunt-express-server')
    grunt.loadNpmTasks('grunt-simple-mocha')
    
    grunt.registerTask('default', ['express:test', 'simplemocha', 'express:test:stop'])
    }
    

    bonus: add 'grunt' as a git pre-commit hook. This way you cannot commit without passing all the tests

    0 讨论(0)
  • 2020-12-13 15:41

    The app.listen method takes a callback that runs when everything is ready. So, you need to be able to pass the done callback there. Something like

    before (done) ->
      var app = require(__dirname + '/../src/app')
      app.listen(3000, done)
    
    0 讨论(0)
  • 2020-12-13 15:44

    Basically you need there are two things you need to do.

    1. Make sure the database connects before the server listens
    2. Make sure the app is started before you run tests.

    The framework already has an a way to handle this by using events.

    Add this to your database connection, which instructs mongoose to emit 'ready' when it's finished connecting.

    mongoose.connection.once('open', function() { 
       // All OK - fire (emit) a ready event. 
       console.log('Connected to MongoDB');
       app.emit('ready'); 
    });
    

    Then instruct express to wait for 'ready' before listening. When it is done it emits: 'appStarted'.

    app.on('ready', function() { 
        app.listen(PORT, function(){ 
            console.log("Server listing on port:",PORT); 
            app.emit("appStarted");
        }); 
    }); 
    

    This is all good practice Mocha aside, to make sure your server starts smoothly. To finish the Mocha integration in your before (I like to use Promises):

    before(function() {        
        return new Promise((resolve,reject) => {
            app.on("appStarted", function(){
                return resolve();
            }); 
        });
    });
    
    0 讨论(0)
提交回复
热议问题