mfncooper / mockery

Simplifying the use of mocks with Node.js
Other
1.1k stars 60 forks source link

enabling specific mocks per test #31

Closed reharik closed 9 years ago

reharik commented 9 years ago

Hi, I know this has been covered a bit before but I don't seem to be able to do this in a reasonable manner. What I would like is this. I have a mut ( module under test ). It takes a dependency on gesPromise. I mock gesPromise with gesPromiseMock. the module is a function which takes args my mock is a function that takes setup args then return a function the stub function. So here is my mock and how i use it.

module.exports = function (result){
    return function(conn, name, skipTake) {
        var data = JSON.stringify({eventName: "someEvent"});
        var newVar = !result ? {
            Status: 'OK',
            NextEventNumber:3,
            Events: [{Event:{EventName:'someEvent',Data: data}},{Event:{EventName:'someEvent',Data: data}},{Event:{EventName:'someEvent',Data: data}}],
            IsEndOfStream: false
        } : result;
        return Promise.resolve(newVar);
    }
};
        var result = {
            Status: 'StreamNotFound',
            NextEventNumber: 500,
            Events: [{}],
            IsEndOfStream: false
        };
        mockery.registerMock('./readStreamEventsForwardPromise', readStreamEventsForwardPromiseMock(result))

so what I would like is to be able to change the "results" for different tests. The only way I've been able to do that so far is with the following set. Note that I must use completely different describe setups.

describe('getEventStoreRepository', function() {
    var mut;
    before(function(){
        mockery.enable({
            warnOnReplace: false,
            warnOnUnregistered: false
        });
        var result = {
            Status: 'StreamNotFound',
            NextEventNumber: 500,
            Events: [{}],
            IsEndOfStream: false
        };
        mockery.registerAllowable('../src/ges/gesRepository', true);
        mockery.registerMock('ges-client', connection);
        mockery.registerMock('./gesPromise', {readStreamEventsForwardPromise: readStreamEventsForwardPromiseMock(result), appendToStreamPromise:appendToStreamPromiseMock} );
    });
describe('#getById_return bad results', function() {
        context('when calling getById with proper args but stream deleted', function (){
            it('should throw proper error', function () {
                mut = require('../src/ges/gesRepository')();
                var id = uuid.v1();
                var streamName = streamNameStrategy(TestAgg.aggregateName(),id);
                var byId = mut.getById(TestAgg, id, 0);
                return byId.must.reject.error(Error, 'Aggregate Deleted: '+streamName);
            })
        });
    });

    after(function () {
        mockery.deregisterAll();
        mockery.disable();
    });
});

now if I do another setup for a test exactly like the above but with different result data it will work. this is a lot of ceremony for each test. Is there any way to do it for each test rather than a whole setup? thanks, r sorry about all the implementation details.

koulmomo commented 9 years ago

you could just have a wrapper object that returns the mock you want, hot-swapping results at run time as opposed to "registerMock" time.

ie

readStreamEventsForwardPromiseMockWrapper

var _result = null;
module.exports = {
    setResult: function (result) {
        _result = result;
    },
    mock: function (conn, name, skipTake) {
        var data = JSON.stringify({eventName: 'someEvent'});

        return Promise.resolve(_result || {
            Status: 'OK',
            NextEventNumber:3,
            Events: [
                {
                    Event:{
                        EventName:'someEvent',
                        Data: data
                    }
                },
                {
                    Event:{
                        EventName:'someEvent',
                        Data: data
                    }
                },
                {
                    Event:{
                        EventName:'someEvent',
                        Data: data
                    }
                }
            ],
            IsEndOfStream: false
        });
    }  
};

Unit Test

var mockWrapper = require('../mocks/readStreamEventsForwardPromiseMockWrapper');
var DEFAULT_RESULT = {
    Status: 'StreamNotFound',
    NextEventNumber: 500,
    Events: [{}],
    IsEndOfStream: false
};

describe('getEventStoreRepository', function() {
    var mut;
    before(function(){
        mockery.enable({
            warnOnReplace: false,
            warnOnUnregistered: false,
            useCleanCache: true
        });

        mockery.registerAllowable('../src/ges/gesRepository', true);
        mockery.registerMock('ges-client', connection);
        mockery.registerMock('./gesPromise', {
            readStreamEventsForwardPromise: mockWrapper.mock,
            appendToStreamPromise: appendToStreamPromiseMock
        });

        mockWrapper.setResult(DEFAULT_RESULT);
        mut = require('../src/ges/gesRepository')();
    });

describe('#getById_return bad results', function() {
    context('when calling getById with proper args but stream deleted', function (){
        it('should throw proper error', function () {
            var id = uuid.v1();
            var streamName = streamNameStrategy(TestAgg.aggregateName(), id);
            var byId = mut.getById(TestAgg, id, 0);
            return byId.must.reject.error(Error, 'Aggregate Deleted: ' + streamName);
        });
    });

    context('when calling getById with proper args but stream foo', function (){
        it('should throw proper error', function () {
            // next time mockWrapper.mock is called, it will return foo
            mockWrapper.setResult({
                status: 'Foo'
            });

            var id = uuid.v1();
            var streamName = streamNameStrategy(TestAgg.aggregateName(), id);
            var byId = mut.getById(TestAgg, id, 0);
            return byId.must.reject.error(Error, 'Stream was foo ' + streamName);
        });
    });

    after(function () {
        mockery.deregisterAll();
        mockery.disable();
    });
});
reharik commented 9 years ago

Ok, I think I see, I construct my mock to take a "switch" then I can modify the mock on the fly. That seems much more succinct. I don't know how it would work if I was using a mock library like sinon. But I haven't been able to get that to work very well anyway. Thanks, R

On Thu, Jun 18, 2015 at 12:43 AM, Mo Kouli notifications@github.com wrote:

you could just have a wrapper object that returns the mock you want, hot-swapping results at run time as opposed to "registerMock" time.

ie readStreamEventsForwardPromiseMockWrapper

var _result = null;module.exports = { setResult: function (result) { _result = result; }, mock: function (conn, name, skipTake) { var data = JSON.stringify({eventName: 'someEvent'});

    return Promise.resolve(_result || {
        Status: 'OK',
        NextEventNumber:3,
        Events: [
            {
                Event:{
                    EventName:'someEvent',
                    Data: data
                }
            },
            {
                Event:{
                    EventName:'someEvent',
                    Data: data
                }
            },
            {
                Event:{
                    EventName:'someEvent',
                    Data: data
                }
            }
        ],
        IsEndOfStream: false
    });
}

};

Unit Test

var mockWrapper = require('../mocks/readStreamEventsForwardPromiseMockWrapper');var DEFAULT_RESULT = { Status: 'StreamNotFound', NextEventNumber: 500, Events: [{}], IsEndOfStream: false };

describe('getEventStoreRepository', function() { var mut; before(function(){ mockery.enable({ warnOnReplace: false, warnOnUnregistered: false, useCleanCache: true });

    mockery.registerAllowable('../src/ges/gesRepository', true);
    mockery.registerMock('ges-client', connection);
    mockery.registerMock('./gesPromise', {
        readStreamEventsForwardPromise: mockWrapper.mock,
        appendToStreamPromise: appendToStreamPromiseMock
    });

    mockWrapper.setResult(DEFAULT_RESULT);
    mut = require('../src/ges/gesRepository')();
});

describe('#getById_return bad results', function() { context('when calling getById with proper args but stream deleted', function (){ it('should throw proper error', function () { var id = uuid.v1(); var streamName = streamNameStrategy(TestAgg.aggregateName(), id); var byId = mut.getById(TestAgg, id, 0); return byId.must.reject.error(Error, 'Aggregate Deleted: ' + streamName); }); });

context('when calling getById with proper args but stream foo', function (){
    it('should throw proper error', function () {
        // next time mockWrapper.mock is called, it will return foo
        mockWrapper.setResult({
            status: 'Foo'
        });

        var id = uuid.v1();
        var streamName = streamNameStrategy(TestAgg.aggregateName(), id);
        var byId = mut.getById(TestAgg, id, 0);
        return byId.must.reject.error(Error, 'Stream was foo ' + streamName);
    });
});

after(function () {
    mockery.deregisterAll();
    mockery.disable();
});

});

— Reply to this email directly or view it on GitHub https://github.com/mfncooper/mockery/issues/31#issuecomment-113044362.