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.

Links:

1. extensible: http://www.jmock.org/jmock1-custom-constraints.html

2. writing a custom constraint: http://www.jmock.org/jmock1-custom-constraints.html