mock library

A simple mocking/spy library.

To create a mock objects for some class T, create a new class using:

class MockT extends Mock implements T {};

Then specify the Behavior of the Mock for different methods using when (to select the method and parameters) and then the Actions for the Behavior by calling thenReturn, alwaysReturn, thenThrow, alwaysThrow, thenCall or alwaysCall.

thenReturn, thenThrow and thenCall are one-shot so you would typically call these more than once to specify a sequence of actions; this can be done with chained calls, e.g.:

 m.when(callsTo('foo')).
     thenReturn(0).thenReturn(1).thenReturn(2);

thenCall and alwaysCall allow you to proxy mocked methods, chaining to some other implementation. This provides a way to implement 'spies'.

For getters and setters, use "get foo" and "set foo"-style arguments to callsTo.

You can disable logging for a particular Behavior easily:

m.when(callsTo('bar')).logging = false;

You can then use the mock object. Once you are done, to verify the behavior, use getLogs to extract a relevant subset of method call logs and apply Matchers to these through calling verify.

A Mock can be given a name when constructed. In this case instead of keeping its own log, it uses a shared log. This can be useful to get an audit trail of interleaved behavior. It is the responsibility of the user to ensure that mock names, if used, are unique.

Limitations:

  • only positional parameters are supported (up to 10);
  • to mock getters you will need to include parentheses in the call (e.g. m.length() will work but not m.length).

Here is a simple example:

class MockList extends Mock implements List {};

List m = new MockList();
m.when(callsTo('add', anything)).alwaysReturn(0);

m.add('foo');
m.add('bar');

getLogs(m, callsTo('add', anything)).verify(happenedExactly(2));
getLogs(m, callsTo('add', 'foo')).verify(happenedOnce);
getLogs(m, callsTo('add', 'isNull)).verify(neverHappened);

Note that we don't need to provide argument matchers for all arguments, but we do need to provide arguments for all matchers. So this is allowed:

m.when(callsTo('add')).alwaysReturn(0);
m.add(1, 2);

But this is not allowed and will throw an exception:

m.when(callsTo('add', anything, anything)).alwaysReturn(0);
m.add(1);

Here is a way to implement a 'spy', which is where we log the call but then hand it off to some other function, which is the same method in a real instance of the class being mocked:

class Foo {
  bar(a, b, c) => a + b + c;
}

class MockFoo extends Mock implements Foo {
  Foo real;
  MockFoo() {
    real = new Foo();
    this.when(callsTo('bar')).alwaysCall(real.bar);
  }
}

However, there is an even easier way, by calling Mock.spy, e.g.:

 var foo = new Foo();
 var spy = new Mock.spy(foo);
 print(spy.bar(1, 2, 3));

Spys created with Mock.spy do not have user-defined behavior; they are simply proxies, and thus will throw an exception if you call when. They capture all calls in the log, so you can do assertions on their history, such as:

  spy.getLogs(callsTo('bar')).verify(happenedOnce);

Classes

Action
The ways in which a call to a mock method can be handled.
Behavior
A Behavior represents how a Mock will respond to one particular type of method call.
CallMatcher
A CallMatcher is a special matcher used to match method calls (i.e. a method name and set of arguments). It is not a Matcher like the unit test Matcher, but instead represents a method name and a collection of Matchers, one per argument, that will be applied to the parameters to decide if the method call is a match.
LogEntry
Every call to a Mock object method is logged. The logs are kept in instances of LogEntry.
LogEntryList
We do verification on a list of LogEntrys. To allow chaining of calls to verify, we encapsulate such a list in the LogEntryList class.
LogEntryListFailure
An exception thrown when an assertion in LogEntryList fails.
Mock
The base class for all mocked objects.
Responder
The behavior of a method call in the mock library is specified with Responders. A Responder has a value to throw or return (depending on the type of action), and can either be one-shot, multi-shot, or infinitely repeating, depending on the value of [count (1, greater than 1, or 0 respectively).

Constants

happenedAtLeastOnce → const Matcher
happenedAtLeastOnce matches one or more calls.
const _TimesMatcher(1)
happenedAtMostOnce → const Matcher
happenedAtMostOnce matches zero or one call.
const _TimesMatcher(0, 1)
happenedOnce → const Matcher
happenedOnce matches exactly one call.
const _TimesMatcher(1, 1)
neverHappened → const Matcher
neverHappened matches zero calls.
const _TimesMatcher(0, 0)

Functions

alwaysReturned(dynamic value) Matcher
alwaysReturned asserts that all matching calls to a method returned a value that matched value.
alwaysThrew(dynamic value) Matcher
alwaysThrew asserts that all matching calls to a method threw a value that matched value.
callsTo([dynamic method, dynamic arg0 = NO_ARG, dynamic arg1 = NO_ARG, dynamic arg2 = NO_ARG, dynamic arg3 = NO_ARG, dynamic arg4 = NO_ARG, dynamic arg5 = NO_ARG, dynamic arg6 = NO_ARG, dynamic arg7 = NO_ARG, dynamic arg8 = NO_ARG, dynamic arg9 = NO_ARG ]) CallMatcher
Returns a CallMatcher for the specified signature. method can be null to match anything, or a literal String, a predicate Function, or a Matcher. The various arguments can be scalar values or Matchers. To match getters and setters, use "get " and "set " prefixes on the names. For example, for a property "foo", you could use "get foo" and "set foo" as literal string arguments to callsTo to match the getter and setter of "foo".
happenedAtLeast(dynamic count) Matcher
happenedAtLeast matches a minimum number of calls.
happenedAtMost(dynamic count) Matcher
happenedAtMost matches a maximum number of calls.
happenedExactly(dynamic count) Matcher
happenedExactly matches an exact number of calls.
neverReturned(dynamic value) Matcher
neverReturned asserts that no matching calls to a method returned a value that matched value.
neverThrew(dynamic value) Matcher
neverThrew asserts that no matching call to a method threw a value that matched value.
returning(dynamic value) Matcher
returning matches log entries where the call to a method returned a value that matched value.
sometimeReturned(dynamic value) Matcher
sometimeReturned asserts that at least one matching call to a method returned a value that matched value.
sometimeThrew(dynamic value) Matcher
sometimeThrew asserts that at least one matching call to a method threw a value that matched value.
throwing(dynamic value) Matcher
throwing matches log entrues where the call to a method threw a value that matched value.

Typedefs

StepValidator(List<LogEntry> logs, int pos) int
StepValidators are used by stepwiseValidate in LogEntryList, which iterates through the list and call the StepValidator function with the log List and position. The StepValidator should return the number of positions to advance upon success, or zero upon failure. When zero is returned an error is reported.