jsmock
v1.0.1
Published
Mocking framework for javascript
Downloads
12
Readme
jsmock
Mocking framework for javascript, inspired by googlemock C++ framework. This project is still under construction ...
Installation
jsmock
is published on npm
npm install --save-dev jsmock
User Guide
All examples provided below assume using mocha
/chai
test framework, although
jsmock
can be used with any framework of your choice.
Mock provides an API to define and verify expectations on all function calls performed on reference to original object. A standard unit test involving mocks will consist of following steps:
- Mock external dependencies of the test subject
- Define expectations on created mocks
- Execute actual tests on the subject
- Verify mock objects
- Cleanup
Creating Mocks
const Mock = require('jsmock').Mock;
let foo = {
bar: (a, b) => {
return a + b
}
};
let fooMock = new Mock(foo);
Now fooMock
is a mock object wrapping foo
. All functions of original object
have been replaced and any call to foo.bar
will cause an UnexpectedCall error
to be thrown.
expect(foo.bar.bind(foo)).to.throw(Error);
Defining Expectations
Expectation
is a main component of a mock oriented test. It describes what kind
of interactions with mocked object are expected during test execution. In addition
Expectation
also defines the actions that should be taken to mock the original
object behavior.
Expectation
for given function is defined by 3 objects:
- Matcher
- Cardinality
- Action List
Matcher decides if current function call is valid for the expectation. Usually it means that function was called with expected argument list. Cardinality describes how many times the expected call should occur. Consequently Action List provides actions to be performed by mocked object for each call.
General Syntax
Expectation is created by calling expectCall
on mock object. Then it can be set up
with dedicated methods:
fooMock.expectCall('bar')
.matching(1, 4)
.times(4)
.willOnce((a, b) => a + b)
.willRepeatedly((a, b) => a - b);
Above code will create expectation on call to foo.bar(1,4)
that should happen
exactly 4 times. On the first call sum of the arguments will be returned, following
3 call with return their difference.
Specifying Matcher
Matcher is an object checking that call of mocked function is valid for given expectation. If no explicit matcher is specified expectation will be executed for any call to mocked function. Matcher can be specified as a predicate or simply as an arguments list to be verified against actual call.
In jsmock matchers come in 3 flavours:
- Predicate Matcher - applies call arguments to provided predicate
- Strict Argument Matcher - call argument list must match exactly matcher argument list
- Weak Argument Matcher - first
N
call arguments must match matcher argument list
Matcher can be specified during Expectation
creation with expectCall
function,
or later with one of available Expectation
methods.
| Method | Description |
|--------|-------------|
| Mock.expectCall(fn, ...args)
| Creates an expectation with Strict Argument Matcher from ...args
|
| Expectation.matching(...args)
| Creates a Strict Argument Matcher from ...args
|
| Expectation.with(...args)
| Alias of Expectation.matching
|
| Expectation.matchingAtLeast(...args)
| Creates a Weak Argument Matcher from ...args
|
| Expectation.withAtLest(...args)
| Alias of Expectation.matchingAtLeast
|
Predicate Matcher is created automatically if one of above methods is called with only one argument that happens to be a function.
fooMock.expectCall('bar', (a,b) => a > b); // Predicate Matcher
foo.bar(3,2); // OK - 3 > 2
foo.bar(1,4); // KO
fooMock.expectCall('bar', 1, 8); // Strict Argument Matcher
foo.bar(1,8); // OK
foo.bar(1,0); // KO
fooMock.expectCall('bar').with(5,6); // Strict Argument Matcher
foo.bar(5,6); // OK
foo.bar(5,1); // KO
fooMock.expectCall('bar').matchingAtLeast(3); // Weak Argument Matcher
foo.bar(3,6); // OK
foo.bar(3,9); // OK
foo.bar(1,3); // KO
Argument Type Matchers
jsmock comes with a family of predefined argument type matchers, that can be helpful if we care more about the type of argument provided to the call than its actual value.
| Matcher | Description |
|---------|-------------|
| Matcher.ANY
| Checks only presence of an argument in a call, doesn't care about actual type |
| Matcher.OBJECT
| Checks if given argument is an object |
| Matcher.NUMBER
| Checks if given argument is a number |
| Matcher.STRING
| Checks if given argument is a string |
| Matcher.BOOLEAN
| Checks if given argument is a boolean |
| Matcher.FUNCTION
| Checks if given argument is a function |
| Matcher.ARRAY
| Checks if given argument is an array |
const Matcher = require('jsmock').Matcher;
fooMock.expectCall('bar').with(Matcher.ANY, Matcher.NUMBER);
foo.bar(1,2); // OK
foo.bar('a',2); // OK
foo.bar([1,2,3], true); // KO
Specifying Cardinality
Cardinality specifies number of expected calls to given function. jsmock
provides
two ways of specifying expectation cardinality. It can be provided explicitly
through one of expectation methods, or it can be calculated automatically from
list of specified actions. If cardinality is specified explicitly it takes precedence
over one calculated from action list.
| Method | Description |
|--------|-------------|
| Expectation.times(N)
| Matching call should occur exactly N
times |
| Expectation.atLeast(N)
| Matching call should occur at least N
times |
| Expectation.atMost(N)
| Matching call should occur at least once and at most N
times |
| Expectation.between(M,N)
| Matching call should occur at least M
times and at most N
times |
fooMock.expectCall('bar').times(2); // Expect bar to be called twice
fooMock.expectCall('bar').atLeast(1); // Expect bar to be called at least one time
fooMock.expectCall('bar').atMost(4); // Expect bar to be called 1 - 4 times
fooMock.expectCall('bar').between(3,5); // Expect bar to be called 3 - 5 times
// Note that
fooMock.expectCall('bar').atMost(MaxCallCount);
// is equivalent of
fooMock.expectCall('bar').between(1, MaxCallCount);
Cardinality can be specified only once for given expectation.
Adding Actions
Action is an object encapsulating function to be executed instead of the original mocked object code. Each expectation can have multiple actions defined with specific cardinality. Actions are executed in the order of creation.
fooMock.expectCall('bar')
.willOnce((a,b) => a * b) // First call will return multiplication of arguments
.willTwice((a,b) => a + b) // Second and third will return sum of arguments
.willRepeatedly((a,b) => b); // All following calls will return second argument
If action specifying method is feed with function it will use it as a callback for actual mocked function execution. If parameter of any other type is provided it will be returned to the caller at execution time.
fooMock.expectCall('bar')
.willOnce(4) // Return 4 on first call
.willTwice(7) // return 7 on next 2 calls
.willRepeatedly(0); // All following calls will return 0
The willRepeatedly method specifies action with unlimited number of potential calls, thus any other attempt to add more actions to the expectation will cause error. Also note that willRepeatedly doesn't return expectation object so it isn't suitable for chaining.
In nodejs
(and js
in general) it's very common to provide callback as the last argument
in the function call. Often the only purpose of the mock is to execute that callback with some
predefined arguments. This kind of action can be created easily using will...Invoke
versions
of action create methods
fsMock.expectCall('readdir')
.willOnceInvoke(null, ['a.js', 'b.js']);
// Which is equivalent of
fsMock.expectCall('readdir')
.willOnce((path, cb) => cb(null, ['a.js', 'b.js']));
| Method | Description |
|--------|-------------|
| Expectation.willOnce(k)
| Adds an action to be executed once |
| Expectation.willTwice(k)
| Adds an action to be executed twice |
| Expectation.willRepeatedly(k)
| Adds an action to be executed until expectation cardinality is fulfilled |
| Expectation.willOnceInvoke(...args)
| Adds an invoker action to be executed once |
| Expectation.willTwiceInvoke(...args)
| Adds an invoker action to be executed twice |
| Expectation.willRepeatedlyInvoke(...args)
| Adds an invoker action to be executed until expectation cardinality is fulfilled |
Verifying Mocks
Mock object will yield errors directly in case of unexpected calls or violation of cardinality upper bound (more calls than expected). Verification of cardinality lower bound has to be done explicitly by the user, at the end of the test.
let foo = new Foo();
let fooMock = new Mock(foo);
fooMock.expectCall('bar')
.times(2) // Lower bound of the cardinality is 2
.willRepeatedly(6);
expect(foo.bar()).to.be.equal(6); // We make only one call to bar
fooMock.verify(); // Will throw Error
Call to verify methods cleans up all previously setup expectations.
Cleaning Mocks
Creation of the mock over an existing object modifies its functions. To restore object to its original state you need to explicitly call cleanup method.
const fs = require('fs');
const Mock = require('jsmock').Mock;
let fsMock = new Mock(fs);
// Setup some expectations
fsMock.expectCall('readdir')
.willOnce((path, cb) => cb(null, ['index.html']));
testedObject.doSomeStuff();
fsMock.verify();
// Once you don't need to use fs in your tests
fsMock.cleanup();
Examples
Some examples of potential "real life" usage can be found in example.js test file.