0

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.

Marcus Hammarberg
  • 4,762
  • 1
  • 31
  • 37

2 Answers2

4

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);
            });             
    });
});
Marcus Hammarberg
  • 4,762
  • 1
  • 31
  • 37
  • 3
    About your blog post: You are right about it is dangerous to use asterisk within version of dependencies. Since I run into big trouble I tell all people it is better to use npm list and add the versions by letting the last digit a x or a min operator before. For example: "test": "*" becomes "test": ">1.0.1" or "1.0.x". I think you should rework your example to be consistent years later. I would expect your reputation will explode and people will try your code without reading the hole post. – Danny May 08 '15 at 10:13
1

This happens because

 var p = yield db.postCollection.findById(id);

is the last line will be executed in your generator function.

You can test whether I am right by adding a console.log('before first yield').

yield is the replacement for return in generator functions, but it runs to the next yield if you call the function a second time.

A generator-function is executed from yield to yield

(best way to explain it the short way - I think).

Your solution: simple erase the yield before the database find:

var p = db.postCollection.findById(id);
Danny
  • 1,078
  • 7
  • 22
  • 2
    in this case the problem with the yield is: the generator is called only once. The examples you can find where you can see code is working after a yield are normally in a line of middleware finally is running in a circle. – Danny Apr 19 '15 at 06:51
  • 1
    ... but earnest I am also confused about this :-) But I think the answer and comments will help you to find your way. – Danny Apr 19 '15 at 06:52
  • Hmmm... I have to use "yield" since that's how my db access works. But yield to yield... How about if I created a small function that only returned a document from Mongo? I'll try that and hit you back. Thanks for being confused - I thought I was going mad for a while – Marcus Hammarberg Apr 20 '15 at 08:15
  • my understanding is based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield what means, the second entering of the generator is necessary. If the db findById would be generator you could use yield* db... (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*). So long my understanding - let me know also if I was wrong for some reasons. – Danny Apr 26 '15 at 14:48
  • Hi, my problem was exactly that... using yield. But I want to use yield. The yield is good. So I had to do more research. Wrote an answer below. Thanks for this input - helped me a lot – Marcus Hammarberg May 06 '15 at 02:12