2

Using mockgoose in a simple unit test is quite straight-forward. However I'm a bit fuzzy as to how one would go about using mockgoose or other mocking solutions in an acceptance or integration test.

Given a simple express/MongoDB app like the following:

/*app.js*/

const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var greetingSchema = mongoose.Schema({
    greeting: String
});

var Greeting = mongoose.model('Greeting', greetingSchema);

app.get('/', function (req, res) {
  Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
    res.send(greeting);
  });  
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

and a simple integration test like this:

/*test.js*/

const app = require('app.js');  
const request = require('supertest');

it('sends "Hello World!" on the response body', (done) => {  
  request(app)
      .get('/')
      .expect(200, 'Hello World!', done);
  });
});

By using the actual app in the request, we are connecting to the app's database ('mongodb://localhost/test'). How then can one use mockgoose, or any other solution, to mock the MongoDB database and still run an integration test like the one shown above?

snowfrogdev
  • 5,963
  • 3
  • 31
  • 58
  • 2
    Note that [Mockgoose](https://github.com/mockgoose/mockgoose) is no longer being supported, use [mongodb-memory-server](https://github.com/nodkz/mongodb-memory-server) instead. – IROEGBU Jul 31 '19 at 09:20

1 Answers1

8

I had the same problem as you. In my case, I solved using chai + chai-http and breaking the db connection and app in different files:

db.js:

const mongoose = require('mongoose');
const config = require('../../config');
mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.DEBUG != undefined);

function open(){
    return new Promise((resolve, reject) => {
        if(process.env.DEBUG != undefined) {
            let Mockgoose = require('mockgoose').Mockgoose;
            let mockgoose = new Mockgoose(mongoose);
            mockgoose.helper.setDbVersion("** your mongodb version **");
            mockgoose.prepareStorage().then(function() {
                mongoose.connect(config.db_test, (err, res) => {
                  if (err) return reject(err);
                  resolve();
                });
            }).catch(reject);
        }else{
            mongoose.connect(config.db, (err, res) => {
              if (err) return reject(err);
              resolve();
            });
        }
    });
}

function close(){
    return mongoose.disconnect();
}

module.exports = { close, open };

app.js:

const express = require('express');

const bodyParser = require('body-parser');
const api = require('./routes');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use('/api', api);

module.exports = app;

test.js (for test):

const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const conn = require('./../utils/db'); // <-- db.js
const app = require('../../app'); // <-- app.js

chai.use(chaiHttp);

describe('# Test', function(){

    before(function(done) {
        conn.open().then(() => done()).catch(done);
    });

    after(function(done){
        conn.close().then(() => done()).catch(done);
    });

    it(`test something`, function(done){

        chai.request(app) // <-- pass the app here
            .get('/path/to/test')
            .then((res) => {
                // expects

                done();
              })
              .catch((err) => {
                  done(err);
              });
    });
});

index.js (for development or production):

const conn = require('./utils/db'); // <-- db.js
const app = require('./app'); // <-- app.js
const config = require('./config'); 

conn.open().then(() => {
    app.listen(config.port, () => {

        // OK!

    });
});

I hope it works for you or anyone.

lmfresneda
  • 449
  • 7
  • 18
  • Awesome. Just wondering, you don't seem have a database connection in your app.js file. Shouldn't you have a call to db.open() from within the app.js file? – snowfrogdev Aug 23 '17 at 21:10
  • 1
    @neoflash I have an index.js with `conn.open()` and `app.listen` after db connection is ok ;) – lmfresneda Aug 24 '17 at 10:24
  • @Imfresneda would you mind adding your index.js to your answer? – snowfrogdev Aug 24 '17 at 10:29
  • 1
    Done! @neoflash – lmfresneda Aug 24 '17 at 10:50
  • @Imfresneda Just got around to trying to implement your solution. I was wondering about the if condition for the mockgoose instance to trigger (process.env.DEBUG != undefined). Where and how do you trigger this condition? In your test file? Somewhere else? Thanks in advance. – snowfrogdev Sep 05 '17 at 02:18
  • 1
    @neoflash Yes, in the command. For example `DEBUG=true mocha`, or if you use windows see you [cross-env](https://www.npmjs.com/package/cross-env) package `cross-env DEBUG=true mocha`. Of course, into the `package.json` file. You can named `NODE_ENV` instead `DEBUG` for convention, but is indifferent. – lmfresneda Sep 05 '17 at 06:46
  • This is one of the better mongodb/mockgoose setup answers I've seen. I'm using it to test with Jest, and aside from 2 'open promises' in mongoose preventing Jest from finishing properly, the tests work. I have it setting up the database in beforeAll, and then afterEach test I have it delete the collections. A bit slower than I'd like, but it got me started, after having a bunch of issues getting it working – Zief Aug 09 '18 at 07:43