Writing Custom Constraints

jMock provides several classes and factory functions that define constraints over the acceptable parameter values of a method invocation1. Sometimes, however, the predefined constraints do not let you specify an expectation accurately enough to keep your tests flexible. In such cases, you can easily define new constraints that seamlessly extend the existing constraints defined by jMock.

A constraint is an object that implements the Constraint2 interface. It does two things:

The org.jmock.constraint4 package contains many constraint implementations and the programmer can easily specify their own constraints by writing their own implementations of the Constraint interface.

To create a new constraint:

  1. Write a class that implements the Constraint interface. The following constraint class tests whether a string starts with a given prefix.

    import org.jmock.core.Constraint;
    
    public class StringStartsWith implements Constraint {
        private String prefix;
    
        public StringStartsWith( String prefix ) {
            this.prefix = prefix;
        }
    
        public boolean eval( Object o ) {
            return o instanceof String && ((String)o).startsWith(prefix);
        }
    
        public StringBuffer describeTo( StringBuffer buffer ) {
            return buffer.append("a string starting with ")
                         .append(Formatting.toReadableString(prefix));
        }
    }

    Note that the describeTo method calls Formatting.toReadableString to format the prefix. The toReadableString method formats any raw value in a way that subtly indicates the value's type and should be used to format any raw values stored in your constraints so that error messages are easy to read.

  2. Write a factory method in your test case that creates an instance of your new constraint.

    public class MyTestCase extends MockObjectTestCase {
        ...
    
        private Constraint aStringStartingWith( String prefix ) {
            return new StringStartsWith(prefix);
        }
    }
  3. Use your factory method to create constraints in your tests. The following expectation specifies that the error method of the mockLogger object must be called once with an argument that is a string starting with "FATAL".

    public class MyTestCase extends MockObjectTestCase {
        ...
    
        public void testSomething() {
            ...
    
            mockLogger.expects(once()).method("error").with( aStringStartingWith("FATAL") );
    
            ...
        }
    
        private Constraint aStringStartingWith( String prefix ) {
            return new StringStartsWith(prefix);
        }
    }

Document History