2015-03-03 13 views
5

Vorrei interrompere il metodo save disponibile per i modelli Mongoose. Ecco un modello di esempio:Stubing del metodo di salvataggio mangusta su un modello

/* model.js */ 
var mongoose = require('mongoose'); 
var userSchema = mongoose.Schema({ 
    username: { 
    type: String, 
    required: true 
    } 
}); 
var User = mongoose.model('User', userSchema); 
module.exports = User; 

ho qualche funzione di supporto che chiamerà il metodo save.

/* utils.js */ 
var User = require('./model'); 
module.exports = function(req, res) { 
    var username = req.body.username; 
    var user = new User({ username: username }); 
    user.save(function(err) { 
    if (err) return res.end(); 
    return res.sendStatus(201); 
    }); 
}; 

vorrei controllare che user.save è chiamato dentro la mia funzione di supporto utilizzando un test di unità.

/* test.js */ 
var mongoose = require('mongoose'); 
var createUser = require('./utils'); 
var userModel = require('./model'); 

it('should do what...', function(done) { 
    var req = { username: 'Andrew' }; 
    var res = { sendStatus: sinon.stub() }; 
    var saveStub = sinon.stub(mongoose.Model.prototype, 'save'); 
    saveStub.yields(null); 

    createUser(req, res); 

    // because `save` is asynchronous, it has proven necessary to place the 
    // expectations inside a setTimeout to run in the next turn of the event loop 
    setTimeout(function() { 
    expect(saveStub.called).to.equal(true); 
    expect(res.sendStatus.called).to.equal(true); 
    done(); 
    }, 0) 
}); 

ho scoperto var saveStub = sinon.stub(mongoose.Model.prototype, 'save') da here.

Tutto va bene a meno che non provi ad aggiungere qualcosa al mio saveStub, ad es. con saveStub.yields(null). Se avessi voluto simulare un errore di essere passato al save callback con saveStub.yields('mock error'), ottengo questo errore:

TypeError: Attempted to wrap undefined property undefined as function 

Lo stack trace è del tutto inutile.

La ricerca che ho fatto

ho tentato di refactoring il mio modello per ottenere l'accesso al modello di utente di base, come raccomandato here. Ciò ha prodotto lo stesso errore per me. Qui è stato il mio codice per quel tentativo:

/* in model.js... */ 
var UserSchema = mongoose.model('User'); 
User._model = new UserSchema(); 

/* in test.js... */ 
var saveStub = sinon.stub(userModel._model, 'save'); 

ho scoperto che this solution non ha funzionato per me a tutti. Forse perché sto configurando il mio modello utente in un modo diverso?

Ho provato anche Mockery a seguito di this guide e this one, ma quello era molto più di quello che pensavo dovesse essere necessario, e mi ha fatto dubitare del valore di passare il tempo a isolare il db.

La mia impressione è che tutto ciò abbia a che fare con il misterioso sistema di attrezzi Mongoose save. Ho letto qualcosa a riguardo usando npm hooks, che rende il metodo save una cosa scivolosa da stubare.

Ho anche sentito parlare di mockgoose, anche se non ho ancora provato questa soluzione. Qualcuno ha avuto successo con quella strategia? [MODIFICA: risulta che mockgoose fornisce un database in memoria per facilitare l'installazione/rimozione, ma non risolve il problema dello stub.]

Qualsiasi opinione su come risolvere questo problema sarebbe molto apprezzata.

risposta

2

Ecco la configurazione finale ho sviluppato, che utilizza una combinazione di Sinon e scherno:

// Dependencies 
var expect = require('chai').expect; 
var sinon = require('sinon'); 
var mockery = require('mockery'); 
var reloadStub = require('../../../spec/utils/reloadStub'); 

describe('UNIT: userController.js', function() { 

    var reportErrorStub; 
    var controller; 
    var userModel; 

    before(function() { 
    // mock the error reporter 
    mockery.enable({ 
     warnOnReplace: false, 
     warnOnUnregistered: false, 
     useCleanCache: true 
    }); 

    // load controller and model 
    controller = require('./userController'); 
    userModel = require('./userModel'); 
    }); 

    after(function() { 
    // disable mock after tests complete 
    mockery.disable(); 
    }); 

    describe('#createUser', function() { 
    var req; 
    var res; 
    var status; 
    var end; 
    var json; 

    // Stub `#save` for all these tests 
    before(function() { 
     sinon.stub(userModel.prototype, 'save'); 
    }); 

    // Stub out req and res 
    beforeEach(function() { 
     req = { 
     body: { 
      username: 'Andrew', 
      userID: 1 
     } 
     }; 

     status = sinon.stub(); 
     end = sinon.stub(); 
     json = sinon.stub(); 

     res = { status: status.returns({ end: end, json: json }) }; 
    }); 

    // Reset call count after each test 
    afterEach(function() { 
     userModel.prototype.save.reset(); 
    }); 

    // Restore after all tests finish 
    after(function() { 
     userModel.prototype.save.restore(); 
    }); 

    it('should call `User.save`', function(done) { 
     controller.createUser(req, res); 
     /** 
     * Since Mongoose's `new` is asynchronous, run our expectations on the 
     * next cycle of the event loop. 
     */ 
     setTimeout(function() { 
     expect(userModel.prototype.save.callCount).to.equal(1); 
     done(); 
     }, 0); 
    }); 
    } 
} 
+0

Ha funzionato per me! Grazie mille per la condivisione! <3 –

1

Hai provato:

sinon.stub(userModel.prototype, 'save') 

Inoltre, in cui è la funzione di supporto sempre chiamato nel test? Sembra che tu definisca la funzione come il modulo utils, ma chiamala come metodo di un oggetto controller. Presumo che questo non abbia nulla a che fare con quel messaggio di errore, ma ha reso più difficile capire quando e dove è stato chiamato lo stub.

+0

Grazie per la risposta. Grazie per la cattura. La differenza nel definire la funzione come modulo e in seguito chiamarla come metodo era un errore introdotto durante la pulizia del codice e la sua semplificazione per SO. L'ho corretto ora. – AndrewSouthpaw

+0

Ho provato a usare 'sinon.stub (userModel.prototype, 'save')'. Cattura accuratamente il comportamento sul metodo 'save', ma getta ancora il' TypeError' che ho descritto prima. – AndrewSouthpaw

Problemi correlati