2

I am trying to test around a mongoose model.

I use proxyquire to mock it integrally, but since the original file register the model, I get

OverwriteModelError: Cannot overwrite `EEG` model once compiled.

Since everything I need is already injected by proxyquire, how can I tell it to not load the model?

Code to test:

var mongoose = require('mongoose');

var db = {},
    Test = require('./model/Test.js'),
    config = require('./config.json');

var dbURL = config.medical.url,
    dbName = config.medical.name;

function connect(callback) {
    db = mongoose.createConnection(dbURL + dbName);
    db.on('error', (err) => {
        callback(err);
    });
    db.once('open', function () {
        callback();
    }); 
}

function save(data, callback) {
    new Test({
        data
    }).save((err) => {
        callback(err);
    });
}

module.exports = {
    connect: connect,
    save: save
}

model/Test.js:

var mongoose = require('mongoose');

//Should not get called when proxyquired
module.exports = mongoose.model('Test', mongoose.Schema({
    date: Date,
    data: String
}));

test/test.js:

//...
describe('save', (done) => {
    it('Expect to successfully save the Test', (done) => {
        var stub = {
                Test: function Test() {
                    this.save = (callback) => {
                        callback();
                    }
                }
            },
            test = proxyquire('./../test.js', {
                './model/Test.js': stub.Test
            });

        test.save({data: 'data'}, (err) => {
            try {
                expect(err).to.not.be.ok;
                done();
            } catch(err) {
                done(err);
            }
        });
    });

    it('Expect to throw any error raised', (done) => {
        var stub = {
                Test: function Test() {
                    this.save = (callback) => {
                        callback('ERROR');
                    }
                }
            },
            //This line raise a error because Test is already compiled
            test = proxyquire('./../test.js', {
                './model/Test.js': stub.Test
            });

        test.save({data: 'data'}, (err) => {
            try {
                expect(err).to.not.be.ok;
                done();
            } catch(err) {
                done(err);
            }
        });
    });
    //...

A alternative method I found would be to proxyquire mongoose in Test, and mock it to prevent the registration, but it would be a lot of code, and I have other test where this model get called 4 or 5 layers deep, so proxyquiring every level would be really cumbersome, while I can mock the highers function.

DrakaSAN
  • 7,673
  • 7
  • 52
  • 94
  • @S.D.: `noCache` is not documented, and doesn't seems to work. Also, I want to keep `callThru` for the others library, I want to block the call thru only on `./model/test.js` – DrakaSAN Jul 12 '16 at 11:53
  • Its actually `var proxyquire = require('proxyquire').noPreserveCache().noCallThru();`. I assume for each test file you require() proxyquire, so you can use a `noCallThru()` only for `test.js` file. – S.D. Jul 12 '16 at 12:41
  • Sadly, I already use `.noPreserveCache`, so that I could regenerate my modules in each test. Weird thing: using a global `noCallThru` work, but trying to use the module only version (`@noCallThru: true` in the proxyquire call) don't. – DrakaSAN Jul 12 '16 at 12:43

1 Answers1

0

Coming back after having found a workaround. I've put the model in a external file, then used this code for test/test.js:

        let stub = {
            mongoose: {
                model: () => {
                    return function Test() {
                        this.save = (callback) => {
                            callback();
                        }
                    }
                },
                Schema: () => {}
            }
        },
        test = proxyquire('./../model/Test.js', {
            'mongoose': stub.mongoose
        }),
        code = proxyquire('./../code.js', {
            'mongoose': mongoose,
            './model/Test.js': test
        });

Basically proxyquire the model to mock mongoose, then proxyquire the code and give it the mocked model.

DrakaSAN
  • 7,673
  • 7
  • 52
  • 94