Appendix A. jMock2 Cheat Sheet

Introduction

We use jMock2 as our mock object framework throughout this book. This chapter summarizes its features and shows some examples of how to use them. We’re using JUnit 4.6 (we assume you’re familiar with it); jMock also supports JUnit3. Full documentation is available at www.jmock.org.

We’ll show the structure of a jMock unit test and describe what its features do. Here’s a whole example:

image

Test Fixture Class

First, we set up the test fixture class by creating its Mockery.

image

For the object under test, a Mockery represents its context—the neighboring objects it will communicate with. The test will tell the mockery to create mock objects, to set expectations on the mock objects, and to check at the end of the test that those expectations have been met. By convention, the mockery is stored in an instance variable named context.

A test written with JUnit4 does not need to extend a specific base class but must specify that it uses jMock with the @RunWith(JMock.class) attribute.1 This tells the JUnit runner to find a Mockery field in the test class and to assert (at the right time in the test lifecycle) that its expectations have been met. This requires that there should be exactly one mockery field in the test class. The class JUnit4Mockery will report expectation failures as JUnit4 test failures.

1. At the time of writing, JUnit was introducing the concept of Rule. We expect to extend the jMock API to adopt this technique.

Creating Mock Objects

This test uses two mock turtles, which we ask the mockery to create. The first is a field in the test class:

private final Turtle turtle = context.mock(Turtle.class);

The second is local to the test, so it’s held in a variable:

final Turtle turtle2 = context.mock(Turtle.class, "turtle2");

The variable has to be final so that the anonymous expectations block has access to it—we’ll return to this soon. This second mock turtle has a specified name, turtle2. Any mock can be given a name which will be used in the report if the test fails; the default name is the type of the object. If there’s more than one mock object of the same type, jMock enforces that only one uses the default name; the others must be given names when declared. This is so that failure reports can make clear which mock instance is which when describing the state of the test.

Tests with Expectations

A test sets up its expectations in one or more expectation blocks, for example:

image

An expectation block can contain any number of expectations. A test can contain multiple expectation blocks; expectations in later blocks are appended to those in earlier blocks. Expectation blocks can be interleaved with calls to the code under test.

Expectations

Expectations have the following structure:

image

The invocation-count and mock-object are required, all the other clauses are optional. You can give an expectation any number of inSequence, when, will, and then clauses. Here are some common examples:

image

Invocation Count

The invocation count is required to describe how often we expect a call to be made during the run of the test. It starts the definition of an expectation.

exactly(n).of

The invocation is expected exactly n times.

oneOf

The invocation is expected exactly once. This is a convenience shorthand for exactly(1).of

atLeast(n).of

The invocation is expected at least n times.

atMost(n).of

The invocation is expected at most n times.

between(min, max).of

The invocation is expected at least min times and at most max times.

allowing

ignoring

The invocation is allowed any number of times including none. These clauses are equivalent to atLeast(0).of, but we use them to highlight that the expectation is a stub—that it’s there to get the test through to the interesting part of the behavior.

never

The invocation is not expected. This is the default behavior if no expectation has been set. We use this clause to emphasize to the reader of a test that an invocation should not be called.

allowing, ignoring, and never can also be applied to an object as a whole. For example, ignoring(turtle2) says to allow all calls to turtle2. Similarly, never(turtle2) says to fail if any calls are made to turtle2 (which is the same as not specifying any expectations on the object). If we add method expectations, we can be more precise, for example:

allowing(turtle2).log(with(anything()));
never(turtle2).stop();

will allow log messages to be sent to the turtle, but fail if it’s told to stop. In practice, while allowing precise invocations is common, blocking individual methods is rarely useful.

Methods

Expected methods are specified by calling the method on the mock object within an expectation block. This defines the name of the method and what argument values are acceptable. Values passed to the method in an expectation will be compared for equality:

oneOf (turtle).turn(45); // matches turn() called with 45
oneOf (calculator).add(2, 2); // matches add() called with 2 and 2

Invocation matching can be made more flexible by using matchers as arguments wrapped in with() clauses:

oneOf(calculator).add(with(lessThan(15)), with(any(int.class)));
// matches add() called with a number less than 15 and any other number

Either all the arguments must be matchers or all must be values:

oneOf(calculator).add(with(lessThan(15)), 22); // this doesn't work!

Argument Matchers

The most commonly used matchers are defined in the Expectations class:

equal(o)

The argument is equal to o, as defined by calling o.equals() with the actual value received during the test. This also recursively compares the contents of arrays.

same(o)

The argument is the same object as o.

any(Class<T> type)

The argument is any value, including null. The type argument is required to force Java to type-check the argument at compile time.

a(Class<T> type)

an(Class<T> type)

The argument is an instance of type or of one of its subtypes.

aNull(Class<T> type)

The argument is null. The type argument is to force Java to type-check the argument at compile time.

aNonNull(Class<T> type)

The argument is not null. The type argument is to force Java to type-check the argument at compile time.

not(m)

The argument does not match the matcher m.

anyOf(m1, m2, m3, [...])

The argument matches at least one of the matchers m1, m2, m3, [...].

allOf(m1, m2, m3, [...])

The argument matches all of the matchers m1, m2, m3, [...].

More matchers are available from static factory methods of the Hamcrest Matchers class, which can be statically imported into the test class. For more precision, custom matchers can be written using the Hamcrest library.

Actions

An expectation can also specify an action to perform when it is matched, by adding a will() clause after the invocation. For example, this expectation will return PEN_DOWN when queryPen() is called:

allowing (turtle).queryPen(); will(returnValue(PEN_DOWN));

jMock provides several standard actions, and programmers can provide custom actions by implementing the Action interface. The standard actions are:

will(returnValue(v))

Return v to the caller.

will(returnIterator(c))

Return an iterator for collection c to the caller.

will(returnIterator(v1, v2, [...], vn))

Return a new iterator over elements v1 to v2 on each invocation.

will(throwException(e))

Throw exception e when called.

will(doAll(a1, a2, [...], an))

Perform all the actions a1 to an on every invocation.

Sequences

The order in which expectations are specified does not have to match the order in which their invocations are called. If invocation order is significant, it can be enforced in a test by adding a Sequence. A test can create more than one sequence and an expectation can be part of more than once sequence at a time. The syntax for creating a Sequence is:

Sequence sequence-variable = context.sequence("sequence-name");

To expect a sequence of invocations, create a Sequence object, write the expectations in the expected order, and add an inSequence() clause to each relevant expectation. Expectations in a sequence can have any invocation count. For example:

image

Here, the queryColor() call is not in the sequence and can take place at any time.

States

Invocations can be constrained to occur only when a condition is true, where a condition is defined as a state machine that is in a given state. State machines can switch between states specified by state names. A test can create multiple state machines, and an invocation can be constrained to one or more conditions. The syntax for creating a state machine is:

image

The initial state is optional; if not specified, the state machine starts in an unnamed initial state.

Add these clauses to expectations to constrain them to match invocations in a given state, or to switch the state of a state machine after an invocation:

when(stateMachine.is("state-name"));

Constrains the last expectation to occur only when stateMachine is in the state "state-name".

when(stateMachine.isNot("state-name"));

Constrains the last expectation to occur only when stateMachine is not in the state "state-name".

then(stateMachine.is("state-name"));

Changes stateMachine to be in the state "state-name" when the invocation occurs.

This example allows turtle to move only when the pen is down:

image

Notice that expectations with states do not define a sequence; they can be combined with Sequence constraints if order is significant. As before, the queryColor() call is not included in the states, and so can be called at any time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.22.160