jMock 1: Constraints
When defining expectations upon your mock objects, jMock forces you to be explicit about the argument values that will be passed to the expected methods. Expected argument values are defined using "constraints". jMock provides a set of constraints that are suitable for almost all tests. These constraints can be combined to tighten or loosen the specification if necessary. The set of constraints is extensible1 so you can write new constraints to cover unusual testing scenarios.
Constraints are specified by calling "syntactic sugar" methods of the MockObjectTestClass. The following constraints are defined by jMock:
Basic Constraints
Object Equality
The most commonly used constraint is eq
, which specifies
that the received argument must be equal to a given value. The code below,
for example, specifies that the method "m" must be called with one
argument of value 1.
mock.expects(once()).method("m").with( eq(1) );
The eq
constraint uses the equals
method of
the expected value to compare the expected and actual values for equality.
Null values are checked beforehand, so it is safe to specify
eq(null)
or apply the constraint to a null actual value. The
eq
constraint is overridden for all primitive types;
primitive values are boxed into objects that are then compared using the
equals
method. Arrays are treated as a special case: two
arrays are considered equal by eq
if they are the same size
and all their elements are considered equal by eq
.
Numeric Equality with Error Margin
An overloaded version of the eq
constraint specifies
floating point values as equal to another value with some margin of error
to account for rounding error. The following code specifies that the
method "m" will be called with one argument of value 1 plus or minus
0.002.
mock.expects(once()).method("m").with( eq(1, 0.002) );
Object Identity
The same
constraint specifies that the actual value of the
argument must be the same object as the expected value. This is a tighter
constraint than eq
, but is usually what you want for
arguments that pass references to behavioural objects. The following code
specifies that method "m" will be called with one argument that refers to
the same object as expected.
Object expected = new Object(); mock.expects(once()).method("m").with( same(expected) );
As a rule of thumb, use eq
for value objects and
same
for behavioural objects.
Instance of a Type
The isA
constraint specifies that the actual argument must
be an instance of the given type. The following code specifies that method
"m" must be called with an argument that is an ActionEvent.
mock.expects(once()).method("m").with( isA(ActionEvent.class) );
String Contains a Substring
The stringContains
constraint specifies that the expected
argument must be a string that contains the given substring. The following
code specifies that method "m" must be called with an argument that is a
string containing the text "hello".
mock.expects(once()).method("m").with( stringContains("hello") );
The stringContains
constraint is especially useful for
testing string contents but isolating tests from the exact details of
punctuation and formatting. For example, the code above would accept any
of the following argument values: "hello world"; "hello, world"; "hello!";
and so on.
Null or Not Null
The constraints NULL
and NOT_NULL
are specify
that an argument is null or is not null, respectively. These are
constants, not methods. The following code specifies that method "m" must
be called with two arguments, the first must be null and the second must
not be null.
mock.expects(once()).method("m").with( NULL, NOT_NULL );
Anything
The ANYTHING
constraint specifies that any value is
allowed. This is useful for ignoring arguments that are not germane to the
scenario being tested. Judicious use of the ANYTHING
constraint can ensure that your tests are flexible and do not require
constant maintenance when tested code changes. The following code
specifies that the method "m" must be called with two arguments, the first
of which is equal to 1 and the second of which is ignored in this
test.
mock.expects(once()).method("m").with( eq(1), ANYTHING );
Combining Constraints
Constraints can be composed to create a tighter or looser specification. Composite constraints are themselves constraints and can therefore be further composed.
Not — Logical Negation
The not
constraint specifies that the actual argument must
not meet a given constraint. The following code specifies that
the method "m" must be called with an argument that is not equal to 1.
mock.expects(once()).method("m").with( not(eq(1)) );
And — Logical Conjunction
The and
constraint specifies that the actual argument must
meet both of two constraints given as arguments. The following
code specifies that the method "m" must be called with a string that
contains the text "hello" and the text "world".
mock.expects(once()).method("m").with( and(stringContains("hello"), stringContains("world")) );
Or — Logical Disjunction
The or
constraint specifies that the actual argument must
meet either of two constraints given as arguments. The following
code specifies that the method "m" must be called with a string that
contains the text "hello" or the text "howdy".
mock.expects(once()).method("m").with( or(stringContains("hello"), stringContains("howdy")) );
More Constraints
That covers all of the constraints provided by jMock. Most of your tests will only need the basic constraints. Sometimes you may need to define constraints by composing existing constraints. Occasionally, however, you might find that no existing constraint exactly meets the need of your test. In these cases, you will have to extend jMock's set of constraints by writing a custom constraint2.