npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

deride

v1.3.0

Published

Mocking library based on composition

Downloads

367

Readme

deride Build Status NPM version Dependency Status Stories in Ready Stories In Progress

NPM

Mocking library based on composition

The inspiration for this was that my colleague was having a look at other mocking frameworks and mentioned to me that they do not work when using Object.freeze in the objects to enforce encapsulation. This library builds on composition to create a mocking library that can work with objects which are frozen.

Getting Started

Install the module with: npm install deride

var deride = require('deride');

Documentation

Mocking

  • deride.wrap(obj)

CAUTION Remember when you use this function about the good practice recommended in the book Growing Object-Oriented Software, Guided by Tests Chapter 8: Only Mock Types That You Own

Expectations

All of the above can be negated e.g. negating the .withArgs would be:

  • obj.expect.method.called.not.withArgs(args)

Resetting the counts / called with args

Setup

Examples

The context

var Person = function(name) {
    return Object.freeze({
        greet: function(otherPersonName) {
            console.log(name, 'says hello to', otherPersonName);
        },
		echo: function(name) {
			return name;
		}
    });
}

Creating a stubbed object

Stubbing an object simply creates an anonymous object, with all the method specified and then the object is wrapped to provide all the expectation functionality of the library

var bob = deride.stub(['greet']);
bob.greet('alice');
bob.expect.greet.called.times(1);

Creating a stubbed object with properties

To stub an object with pre set properties call the stub method with a properties array in the second parameter. We are following the defineProperty definition as can be found in the below link.

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

var bob = deride.stub(['greet'], [{name: 'age', options: { value: 25, enumerable: true}}]);
bob.age === 25;

Creating a stubbed object based on an existing object

var Person = {
    greet: function(name) {
        return 'alice sas hello to ' + name;
    },
};
var bob = deride.stub(Person);
bob.greet('alice');
bob.expect.greet.called.once();

Creating a single mocked method

var func = deride.func();
func.setup.toReturn(1);
var value = func(1, 2, 3);
assert.equal(value, 1);

Wrapping an existing function

var f = function (name) { return 'hello ' + name; };
var func = deride.func(f);
assert(func('bob'), 'hello bob');
func.expect.called.withArg('bob');

Wrapping an existing promise function

var f = function (name) { return 'hello ' + name; };
var func = deride.func(when.lift(f));
func('bob').then(function (result) {
    assert(result, 'hello bob');
    func.expect.called.withArg('bob');
}).finally(done);

Events

Force the emit of an event on an object

var bob = deride.stub([]);
bob.on('message', function() {
    done();
});
bob.emit('message', 'payload');

Emit an event on method invocation

bob.setup.greet.toEmit('testing');
bob.on('testing', function() {
	done();
});
bob.greet('bob');

Emit an event with args on method invocation

bob.setup.greet.toEmit('testing', 'arg1', { a: 1 });
bob.on('testing', function(a1, a2) {
	a1.should.eql('arg1');
	a2.should.eql({ a: 1 });
	done();
});
bob.greet('bob');

Count the number of invocations of a method

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice');
bob.expect.greet.called.times(1);

Has convenience methods for invocation counts

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice');
bob.expect.greet.called.once();
bob.greet('sally');
bob.expect.greet.called.twice();

Handy lt, lte, gt and gte methods

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice');
bob.greet('alice');
bob.greet('alice');

bob.expect.greet.called.lt(4);
bob.expect.greet.called.lte(3);
bob.expect.greet.called.gt(2);
bob.expect.greet.called.gte(3);

Determine if a method has never been called

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.expect.greet.called.never();

Resetting the called count on all methods

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice');
bob.echo('alice');
bob.expect.greet.called.once();
bob.expect.echo.called.once();

bob.called.reset();

bob.expect.greet.called.never();
bob.expect.echo.called.never();

Determine if a method was called with a specific set of arguments

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice');
bob.greet('bob');
bob.expect.greet.called.withArgs('bob');

Determine if a method was called with the exact set of arguments

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.greet('alice', ['james'], 987);
bob.expect.greet.called.matchExactly('alice', ['james'], 987);

Override the method body to change the invocation

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toDoThis(function(otherPersonName) {
    return util.format('yo %s', otherPersonName);
});
var result = bob.greet('alice');
result.should.eql('yo alice');

Override the return value for a function

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toReturn('foobar');
var result = bob.greet('alice');
result.should.eql('foobar');

Overriding the promise resolver for a function

To resolve with a value

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toResolveWith('foobar');
bob.greet('alice').then(function(result) {
    result.should.eql('foobar');
});

To reject with a value

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toRejectWith('foobar');
bob.greet('alice').catch(function(result) {
    result.should.eql('foobar');
});

Force a method invocation to throw a specific error

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toThrow('BANG');
should(function() {
    bob.greet('alice');
}).
throw(/BANG/);

Override the invocation of a callback

when there is only one function passed as args

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.chuckle.toCallbackWith(0, 'boom');
bob.chuckle(function(err, message) {
    assert.equal(err, 0);
    assert.equal(message, 'boom');
});

when the callback is the last arg which is a function

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.chuckle.toCallbackWith(0, 'boom');
bob.chuckle('bob', function() {
    done('this was not the callback');
}, function(err, message) {
    assert.equal(err, 0);
    assert.equal(message, 'boom');
    done();
});

Accelerating the timeout used internally by a function

var Person = function(name) {
    return Object.freeze({
        foobar: function(timeout, callback) {
            setTimeout(function() {
                callback('result');
            }, timeout);
        }
    });
};
var timeout = 10000;
var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.foobar.toTimeWarp(timeout);
bob.foobar(timeout, function(message) {
    assert.equal(message, 'result');
});

Setup an intercept

Currently this will allow you to inspect the arguments that are passed to a method, but it will not pass any modifications to the real method.

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.toIntercept(function () {
    console.log(arguments); // { '0': 'sally', '1': { message: 'hello %s'} }
});

bob.greet('sally', {message: 'hello %s'});

Setup for specific arguments

Setting the return value of a function when specific arguments are used

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.when('alice').toReturn('foobar');
bob.setup.greet.toReturn('barfoo');
var result1 = bob.greet('alice');
var result2 = bob.greet('bob');
result1.should.eql('foobar');
result2.should.eql('barfoo');

Overriding a method's body when specific arguments are provided

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.when('alice').toDoThis(function(otherPersonName) {
    return util.format('yo yo %s', otherPersonName);
});
bob.setup.greet.toDoThis(function(otherPersonName) {
    return util.format('yo %s', otherPersonName);
});
var result1 = bob.greet('alice');
var result2 = bob.greet('bob');
result1.should.eql('yo yo alice');
result2.should.eql('yo bob');

Throwing an error for a method invocation when specific arguments are provided

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.greet.when('alice').toThrow('BANG');
should(function() {
    bob.greet('alice');
}).
throw (/BANG/);
should(function() {
    bob.greet('bob');
}).not.
throw (/BANG/);

Override the invocation of a callback when specific arguments are provided

var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.chuckle.toCallbackWith([0, 'boom']);
bob.setup.chuckle.when('alice').toCallbackWith([0, 'bam']);
bob.chuckle(function(err, message) {
    assert.equal(err, 0);
    assert.equal(message, 'boom');
    bob.chuckle('alice', function(err, message) {
        assert.equal(err, 0);
        assert.equal(message, 'bam');
    });
});

Accelerating the timeout used internally by a function when specific arguments are provided

var Person = function(name) {
    return Object.freeze({
        foobar: function(timeout, callback) {
            setTimeout(function() {
                callback('result');
            }, timeout);
        }
    });
};
var timeout1 = 10000;
var timeout2 = 20000;
var bob = new Person('bob');
bob = deride.wrap(bob);
bob.setup.foobar.toTimeWarp(timeout1);
bob.setup.foobar.when(timeout2).toTimeWarp(timeout2);
bob.foobar(timeout1, function(message) {
    assert.equal(message, 'result');
    bob.foobar(timeout2, function(message) {
        assert.equal(message, 'result');
        done();
    });
});

Use a function as a predicate

If a function is passed to the when, then this will be invoked with the arguments passed. The function that has been setup will be called if this predicate returns truthy.

function resourceMatchingPredicate(msg) {
	var content = JSON.parse(msg.content.toString());
	return content.resource === 'talula';
}
bob.setup.chuckle.toReturn('chuckling');
bob.setup.chuckle.when(resourceMatchingPredicate).toReturn('chuckle talula');

var matchingMsg = {
	//...
	//other properties that we do not know until runtime
	//...
	content: new Buffer(JSON.stringify({
		resource: 'talula'
	}))
};
bob.chuckle(matchingMsg).should.eql('chuckle talula');

Setting multiple functions as a predicates

function tatulaMatchingPredicate(msg) {
	var content = JSON.parse(msg.content.toString());
	return content.resource === 'talula';
}
function babulaMatchingPredicate(msg) {
	var content = JSON.parse(msg.content.toString());
	return content.resource === 'babula';
}
bob.setup.chuckle.toReturn('chuckling');
bob.setup.chuckle.when(tatulaMatchingPredicate).toReturn('chuckle talula');
bob.setup.chuckle.when(babulaMatchingPredicate).toReturn('chuckle babula');

var matchingMsg = {
	//...
	//other properties that we do not know until runtime
	//...
	content: new Buffer(JSON.stringify({
		resource: 'talula'
	}))
};
bob.chuckle(matchingMsg).should.eql('chuckle talula');
matchingMsg.content.resource = 'babula';
bob.chuckle(matchingMsg).should.eql('chuckle babula');

Using a stub X times

bob.setup.greet
    .toReturn('alice')
    .twice()
    .and.then
    .toReturn('sally');
bob.greet().should.eql('alice');
bob.greet().should.eql('alice');
bob.greet().should.eql('sally');
bob.greet().should.eql('sally');

Using a stub when

var normalResult = bob.greet('talula');
var normalSimon = bob.greet('simon');
bob.setup.greet
    .when('simon')
    .toReturn('alice')
    .twice();
// default Person behaviour
should(bob.greet('talula')).eql(normalResult);
// overridden behaviour
bob.greet('simon').should.eql('alice');
bob.greet('simon').should.eql('alice');
// default Person behaviour
should(bob.greet('simon')).eql(normalSimon);

Specify to fallback

var normalResult = bob.greet('alice');
debug('normalResult', normalResult);
bob.setup.greet
    .toReturn('alice')
    .twice()
    .and.then
    .fallback();
bob.greet('alice').should.eql('alice');
bob.greet('alice').should.eql('alice');
should(bob.greet('alice')).eql(normalResult);

Provide access to individual calls to a method

var bob = deride.wrap(bob);
bob.greet('jack', 'alice');
bob.greet('bob');
bob.expect.greet.invocation(0).withArg('alice');
bob.expect.greet.invocation(1).withArg('bob');

Enable the assertion on a single arg being used in any invocation

when the arg is a primitive object

var bob = deride.wrap(bob);
bob.greet('alice', {
    name: 'bob',
    a: 1
}, 'sam');
bob.expect.greet.called.withArg('sam');

when the arg is not a primitive object

var bob = deride.wrap(bob);
bob.greet('alice', {
    name: 'bob',
    a: 1
});
bob.expect.greet.called.withArg({
    name: 'bob'
});

when the arg is a primitive object

var bob = deride.stub(['greet']);
bob.greet('The inspiration for this was that my colleague was having a');
bob.greet({a: 123, b: 'talula'}, 123, 'something');

bob.expect.greet.called.withMatch(/^The inspiration for this was/);

when the arg is not a primitive object

var bob = deride.stub(['greet']);
bob.greet('The inspiration for this was that my colleague was having a');
bob.greet({a: 123, b: { a: {'talula'}}, 123, 'something');

bob.expect.greet.called.withMatch(/^talula/gi);

Contributing

Please ensure that you run grunt, have no style warnings and that all the tests are passing.

License

Copyright (c) 2014 Andrew Rea
Copyright (c) 2014 James Allen

Licensed under the MIT license.