Howard Dierking
Howard Dierking

Reputation: 1224

Mocha async test run twice when an adjacent test is present

This one is frustratingly puzzling.

I have the following test fixture:

describe('#post', function(){
    var options,
        bodyContent,
        bodyWriter;

    beforeEach(function(){
        // setup common objects ...
    });

    it('should have request body', function(done){
        httpHelper.post(options, bodyWriter, function(err, data){
            should.not.exist(err);
            should.exist(requestData.body);
            requestData.body.should.eql(bodyContent);
            done();
        });
    });

    // ...
});

Now, this works just fine - right up until the point where I add another test:

it('should contain path from options arg', function(done){
    httpHelper(options, bodyWriter, function(err, data){
        should.not.exist(err);
        requestData.options.path.should.eql(options.path);
        done();
    });
});

Now when I run the fixture, I get the following:

http
    #post
      ✓ should require options
      ✓ should have body
      1) should have body
      ✓ should contain path from options arg

I have no idea why this test is being run twice. Any thoughts?

Upvotes: 0

Views: 1838

Answers (1)

Howard Dierking
Howard Dierking

Reputation: 1224

Thanks to @Louis's comment, I was able to identify the problem. One of the things I was doing in the test module was faking the native https module and injecting it using https://github.com/felixge/node-sandboxed-module. The problem was in my fake.

var fakeHttpsModule = (function(){
    var response = new EventEmitter(); 
    response.setEncoding = function(val){ /* no op */ };

    var request = function(options, callback) { 
        requestData.options = options;

        callback(response);
        return {
            write: function(value){
                requestData.body += value;
                response.emit('data', value);
            },
            end: function(){
                response.emit('end');
            },
            on: function(event, callback){ /* no op */ }
        };
    };

    return {
        request: request
    };
})();

The problem was based on the scope of the response object. By scoping it to the module, every time a test called the request method, that test's callback would ended up getting added as a registration to the EventEmitter. Hence, every test after the first one that called this method would get the error that Done() was called more than once.

The solution was to simply move the declaration of response so that it was scoped to the request function as follows.

var fakeHttpsModule = (function(){      
    var request = function(options, callback) { 
        requestData.options = options;

        var response = new EventEmitter(); 
        response.setEncoding = function(val){ /* no op */ };

        callback(response);
        return {
            write: function(value){
                requestData.body += value;
                response.emit('data', value);
            },
            end: function(){
                response.emit('end');
            },
            on: function(event, callback){ /* no op */ }
        };
    };

    return {
        request: request
    };
})();

Upvotes: 1

Related Questions