title: Mocking/stubbing Mongoose model save method
body: <p>Given a simple Mongoose model:</p>
  
  <pre><code>import mongoose, { Schema } from 'mongoose';
  
  const PostSchema = Schema({
    title:    { type: String },
    postDate: { type: Date, default: Date.now }
  }, { timestamps: true });
  
  const Post = mongoose.model('Post', PostSchema);
  
  export default Post;
  </code></pre>
  
  <p>I wish to test this model, but I'm hitting a few roadblocks.</p>
  
  <p>My current spec looks something like this (some stuff omitted for brevity):</p>
  
  <pre><code>import mongoose from 'mongoose';
  import { expect } from 'chai';
  import { Post } from '../../app/models';
  
  describe('Post', () =&gt; {
    beforeEach((done) =&gt; {
      mongoose.connect('mongodb://localhost/node-test');
      done();
    });
  
    describe('Given a valid post', () =&gt; {
      it('should create the post', (done) =&gt; {
        const post = new Post({
          title: 'My test post',
          postDate: Date.now()
        });
  
        post.save((err, doc) =&gt; {
          expect(doc.title).to.equal(post.title)
          expect(doc.postDate).to.equal(post.postDate);
          done();
        });
      });
    });
  });
  </code></pre>
  
  <p>However, with this I'm hitting my database every time I run the test, which I would prefer to avoid.</p>
  
  <p>I've tried using <a href="https://github.com/mccormicka/Mockgoose/" rel="noreferrer">Mockgoose</a>, but then my test won't run.</p>
  
  <pre><code>import mockgoose from 'mockgoose';
  // in before or beforeEach
  mockgoose(mongoose);
  </code></pre>
  
  <p>The test gets stuck and throws an error saying: <code>Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.</code> I've tried increasing the timeout to 20 seconds but that didn't solve anything.</p>
  
  <p>Next, I threw away Mockgoose and tried using Sinon to stub the <code>save</code> call.</p>
  
  <pre><code>describe('Given a valid post', () =&gt; {
    it('should create the post', (done) =&gt; {
      const post = new Post({
        title: 'My test post',
        postDate: Date.now()
      });
  
      const stub = sinon.stub(post, 'save', function(cb) { cb(null) })
      post.save((err, post) =&gt; {
        expect(stub).to.have.been.called;
        done();
      });
    });
  });
  </code></pre>
  
  <p>This test passes, but it somehow doesn't make much sense to me. I'm quite new to stubbing, mocking, what have you, ... and I'm not sure if this is the right way to go. I'm stubbing the <code>save</code> method on <code>post</code>, and then I'm asserting it to have been called, but I'm obviously calling it... Also, I can't seem to get to the arguments the non-stubbed Mongoose method would return. I would like to compare the <code>post</code> variable to something the <code>save</code> method returns, like in the very first test where I hit the database. I've tried a <a href="https://stackoverflow.com/questions/22340419/stub-save-instance-method-of-mongoose-model-with-sinon">couple</a> of <a href="https://stackoverflow.com/questions/11318972/stubbing-a-mongoose-model-with-sinon">methods</a> but they all feel quite hackish. There must be a clean way, no?</p>
  
  <p>Couple of questions:</p>
  
  <ul>
  <li><p>Should I indeed avoid hitting the database like I've always read everywhere? My first example works fine and I could clear the database after each run. However, it doesn't really feel right to me.</p></li>
  <li><p>How would I stub the save method from the Mongoose model and make sure it actually tests what I want to test: saving a new object to the db.</p></li>
  </ul>