Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Test with Mongoose

I'm new to Node.js, Mongoose, and testing in this environment. I have the following schema declared in a separate file.

Issue = mongoose.model("Issue", {
  identifier: String,
    date: String,
    url: String,    
    name: String,
    thumbnailURL: String
});

Then I have this method which simply returns all of the Issue instances in the MongoDB collection.

function issues(request, response) {
  response.setHeader('Content-Type', 'text/json');

  Issue.find().sort('date').exec(function(error, items) {
    if (error) {
      response.send(403, {"status": "error", "error:": exception});
    }
    else {
      response.send(200, {"issues": items});
    }
  });
}

I've gotten this far through experimentation, and now I want to test it, but I've run into a problem. How do I go about testing it, without setting up a MongoDB connection, etc. I know that I can set all that stuff up, but that's an integration test. I want to write unit tests to test things like:

  • Does the function set the content type correctly
  • Does the function sort by the date field
  • Does the function return a 403 when an error occurs?
  • ... and so on

I'm curious to see how I could refactor my existing code to make it more unit testable. I've tried maybe creating a second function that's called through, accepting the response and Item schema objects as parameters, but it doesn't feel right. Anyone have any better suggestions?

like image 318
Ash Furrow Avatar asked Oct 09 '13 22:10

Ash Furrow


3 Answers

Mongoose model (your Issue) returns a new instance of the Query object. The new query instance has access to the exec method through the prototype. (mongoose 3.8~)

If you want to return an error you can write:

sinon.stub(mongoose.Query.prototype, "exec").yields({ name: "MongoError" }, null);
like image 62
Emil A. Avatar answered Nov 19 '22 16:11

Emil A.


Using mocha with chaijs and sinonjs in my node code something like this method works for me:

var should = require('chai').should(),
sinon = require('sinon'),
mongoose = require('mongoose');

it('#issues() handles mongoosejs errors with 403 response code and a JSON error message', function (done) {

// mock request
var request = {};

// mock response
var response = {};
response.setHeader = function(header) {};
response.send = function (responseCode, jsonObject) {
    responseCode.should.equal(403);
    jsonObject.stats.should.equal('error');
    // add a test for "error:": exception
    done();
}

var mockFind = {
    sort: function(sortOrder) {
        return this;
    },
    exec: function (callback) {
        callback('Error');
    }
}

// stub the mongoose find() and return mock find 
mongoose.Model.find = sinon.stub().returns(mockFind);

// run function
issues(request, response);

});
like image 31
Rudi Starcevic Avatar answered Nov 19 '22 16:11

Rudi Starcevic


I'm not sure how to test the Content-Type, and I haven't tested this code myself, but I'm happy to help out if it doesn't work. It seems to make sense to me. Basically we just created a callback so we could move the response.send out of the actual custom logic, then we can test via that callback. Let me know if it doesn't work or make sense. You can use the links that the other guys posted to prevent having to connect to the db.

Issue = mongoose.model("Issue", {
    identifier: String,
    date: String,
    url: String,    
    name: String,
    thumbnailURL: String
  });

  function issues(callback, request, response) {
    Issue.find().sort('number').exec(function(error, items) {
      if (error) {
        callback(403, {"status": "error", "error:": exception});
      }
      else {
        callback(200, {"issues": items});
      }
    });
  }

  //Note: probably don't need to make a whole new `sender` function - depends on your taste
  function sender(statusCode, obj) {
    response.setHeader('Content-Type', 'text/json');
    response.send(statusCode, obj);
  }

  //Then, when you want to implement issues
  issues(sender, request, response);

  //The tests - will depend on your style and test framework, but you get the idea
  function test() {
    //The 200 response
    issues(function(code, obj) {
      assert(code === 200);
      assert(obj.issues === ??);   //some testable value, or just do a truthy test
      //Here, you can also compare the obj.issues item to the proper date-sorted value
    });

    //The error response
    issues(function(code, obj) {
      assert(code === 403);
      assert(code.status === 'error');
    });
  }
like image 1
matthewpalmer Avatar answered Nov 19 '22 15:11

matthewpalmer