Search code examples
node.jssupertestmonkco

Using supertest and co to validate database content after request


I want to write a test to update a blog post (or whatever): * Insert a blog post in a database * Get the id the blog post got in MongoDb * POST an updated version to my endpoint * After the request have finished: check in the database that update has been done

Here's this, using koa:

var db = require('../lib/db.js');
describe('a test suite', function(){
    it('updates an existing text', function (done) {
      co(function * () {
        var insertedPost = yield db.postCollection.insert({ title : "Title", content : "My awesome content"});
        var id =  insertedPost._id;
        var url = "/post/" + id;
        var updatedPost = { content : 'Awesomer content' };

        request
            .post(url)
            .send(updatedTextData)
            .expect(302)
            .expect('location', url)
            .end(function () {
                co(function *() {
                    var p = yield db.postCollection.findById(id);
                    p.content.should.equal(updatedPost.content);
                    console.log("CHECKED DB");
                })(done());
            });
        });
    });
});

I realize that there's a lot of moving parts in there, but I've tested all the interactions separately. Here's the db-file I've included (which I know works fine since I use it in production):

var monk = require('monk');
var wrap = require('co-monk');

function getCollection(mongoUrl, collectionName) {
    var db = monk(mongoUrl);
    return wrap(db.get(collectionName));
};

module.exports.postCollection   = getCollection([SECRET MONGO CONNECTION], 'posts');

The production code works as intended. This test passes but it seems, to me, like the co-function in the .end()-clause never is run... but the done() call gets made. No "CHECKED DB" is being printed, at least.

I've tried with "done()" and "done" without. Sometimes that works and sometimes not. I've tried to move the check of the database outside the request... but that just hangs, since supertest wants us to call done() when we are completed.

All of this leaves me confused and scared (:)) - what am I doing wrong here.


Solution

  • Realising that the question was very long-winding and specific I feared that I would never get a proper answer. Due to the badly asked question.

    But the answer given and the comments made me look again and I found it. I wrote a long blog post about it but I'll give away the end of it here as a summary. If it doesn't make sense there's more of the same :) in the blog post.

    Here is the TL;DR:

    I wanted to check the state of the database after doing a request. This can be done using the .end() function of supertest.

    Since I used co-monk I wanted to be able to do that using yield and generators. This means that I need to wrap my generator function with co.

    co, since version 4.0.0, returns a promise. This perfect for users of mocha since it allows us to use the .then() function and pass the done variable to both the success and failure functions of .then(fn success, fn failure(err)).

    The test in it’s entirety is displayed below. Running this returns the error due to failing assertion, as I want:

    var co = require("co");
    var should = require("should");
    var helpers = require('./testHelpers.js');
    var users = helpers.users;
    var request = helpers.request;
    
    describe('POST to /user', function(){
    
        var test_user = {};
    
        beforeEach(function (done) {
            test_user = helpers.test_user;
            helpers.removeAll(done);
        });
    
        afterEach(function (done) {
            helpers.removeAll(done);
        });
    
        it('creates a new user for complete posted data', function(done){
            // Post
            request
                .post('/user')
                .send(test_user)
                .expect('location', /^\/user\/[0-9a-fA-F]{24}$/) // Mongo Object Id /user/234234523562512512
                .expect(201)
                .end(function () {
                    co(function *() {
                        var userFromDb = yield users.findOne({ name : test_user.name });
                        userFromDb.name.should.equal("This is not the name you are looking for");
                    }).then(done, done);
                });             
        });
    });