jMock 2 Cheat Sheet
Test Fixture Class
Raw
import org.jmock.Mockery;
import org.jmock.Expectations;
public class AJMock2TestCase ... {
Mockery context = new Mockery();
...
}
JUnit 3 Integration
import org.jmock.integration.junit3.MockObjectTestCase;
import org.jmock.Expectations;
public class TurtleDrawingTest extends MockObjectTestCase {
...
}
JUnit 4 Integration
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
@RunWith(JMock.class)
public class AJMock2TestCase {
Mockery context = new JUnit4Mockery();
...
}
A Mockery
represents the context
of the object under test: the objects that it communicates with. A Mockery
creates mock objects and checks expectations that are set upon those mock
objects. By convention the Mockery is stored in an instance variable named
context.
Tests written with JUnit 3 can
extend MockObjectTestCase, in which case they don't need to explictly
create or refer to the context.
Tests written with
JUnit 4 do not need to extend a specific base class but must specify that
they use jMock with the @RunWith
attribute, create a
JUnit4Mockery that reports expectation failures as JUnit 4 test failures,
and store the Mockery in an instance variable.
Creating Mock Objects
Raw
Turtle turtle = context.mock(Turtle.class);
Turtle turtle2 = context.mock(Turtle.class, "turtle2");
JUnit 3 Integration
Turtle turtle = mock(Turtle.class);
Turtle turtle2 = mock(Turtle.class, "turtle2");
JUnit 4 Integration
Turtle turtle = context.mock(Turtle.class);
Turtle turtle2 = context.mock(Turtle.class, "turtle2");
The examples above assume that the mock object is stored in an instance
variable. If a mock object is stored in a local variable, the variable
must be declared as final
so that it can be referred to from
within expectation blocks (see below).
Tests with Expectations
Raw
... create mock objects ...
public void testSomeAction() {
... set up ...
context.checking(new Expectations() {{
... expectations go here ...
}});
... call code being tested ...
context.assertIsSatisfied();
... other assertions ...
}
JUnit 3 Integration
... create mock objects ...
public void testSomeAction() {
... set up ...
checking(new Expectations() {{
... expectations go here ...
}});
... call code being tested ...
... assertions ...
}
JUnit 4 Integration
... create mock objects ...
public void testSomeAction() {
... set up ...
context.checking(new Expectations() {{
... expectations go here ...
}});
... call code being tested ...
... assertions ...
}
The JUnit 3 and JUnit 4 integration layers
automatically assert that all expectations have been satisfied.
An expectations 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. Expectations can be interspersed with
calls to the code under test.
Expectations
Expectations have the following structure:
invocation-count (mock-object).method(argument-constraints);
inSequence(sequence-name);
when(state-machine.is(state-name));
will(action);
then(state-machine.is(new-state-name));
Except for the invocation count and the mock object, all clauses are
optional. You can give an expectation as many inSequence
,
when
, will
and then
clauses as you
wish.
Some examples:
one (turtle).turn(45); // The turtle will be told to turn 45 degrees once only
allowing (turtle).flashLEDs(); // The turtle can be told to flash its LEDs any number of types or not at all
ignoring (turtle2); // Turtle 2 can be told to do anything. This test ignores it.
allowing (turtle).queryPen(); // The turtle can be asked about its pen any number of times and will always
will(returnValue(PEN_DOWN)); // return PEN_DOWN
atLeast(1).of (turtle).stop(); // The turtle will be told to stop at least once.
Invocation Count
one |
The invocation is expected once and once only. |
exactly(n).of |
The invocation is expected exactly n times. Note:
one is a convenient shorthand for
exactly(1) . |
atLeast(n).of |
The invocation is expected at least n. |
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 |
The invocation is allowed any number of times but does not have
to happen. |
ignoring |
The same as allowing . Allowing or ignoring should
be chosen to make the test code clearly express intent. |
never |
The invocation is not expected at all. This is used to make
tests more explicit and so easier to understand. |
Methods And Expectations
Expected methods are specified by a literal call to the method within
an expectation block.
Arguments passed to the method in an expectation will be compared for
equality.
one (calculator).add(1, 1); will(returnValue(2));
one (calculator).add(2, 2); will(returnValue(5));
To define looser constraints, specify all arguments as
matchers within with
clauses:
allowing (calculator).add(with(any(int.class)), with(any(int.class)));
Argument Matchers
The most commonly used matchers are defined in the Expectations1
class:
equal(n) |
The argument is equal to n. |
same(o) |
The argument is the same object as o. |
any(Class<T> type) |
The argument is any value. 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 a subclass of
type. |
aNull(Class<T> type) |
The argument is null. The type argument is required to force
Java to type-check the argument at compile time. |
aNonNull(Class<T> type) |
The argument is not null. The type argument is required to force
Java to type-check the argument at compile time. |
not(m) |
The argument does not match the Matcher m. |
anyOf(m1, m2, ...,
mn) |
The argument matches one of the Matchers
m1 to
mn. |
allOf(m1, m2, ...,
mn) |
The argument matches all of the Matchers
m1 to mn. |
More matchers are defined as static factory methods of the Hamcrest Matchers
class2, which can be statically imported at the top of the test
code.
Actions
will(returnValue(v)) |
Return v to the caller. |
will(returnIterator(c)) |
Return a new iterator over collection c on each
invocation. |
will(returnIterator(v1,
v2, ..., vn)) |
Return a new iterator over elements v1 to
vn on each invocation. |
will(throwException(e)) |
Throw e to the caller. |
will(doAll(a1, a2,
..., an)) |
Do all actions a1 to
an on every invocation. |
Sequences
Invocations that are expected in a sequence must occur in the order in
which they appear in the test code. A test can create more than one
sequence and an expectation can be part of more than once sequence at a
time.
To define a new sequence:
Raw
final Sequence sequence-name = context.sequence("sequence-name");
JUnit 3
final Sequence sequence-name = sequence("sequence-name");
JUnit 4
final Sequence sequence-name = context.sequence("sequence-name");
To expect a sequence of invocations, write the expectations in order
and add the inSequence(sequence)
clause to each
one. For example:
one (turtle).forward(10); inSequence(drawing);
one (turtle).turn(45); inSequence(drawing);
one (turtle).forward(10); inSequence(drawing);
Expectations in a sequence can have any invocation count.
States
States are used to constrain invocations to occur only when a condition
is true. Conditions are represented as states of state machines. A test
can create multiple state machines and each state machine can have
multiple states. An invocation can be constrained to occur during a state
of one more more state machines.
To define a new state machine:
Raw
final States state-machine-name = context.states("state-machine-name").startsAs("initial-state");
JUnit 3
final States state-machine-name = states("state-machine-name").startsAs("initial-state");
JUnit 4
final States state-machine-name = context.states("state-machine-name").startsAs("initial-state");
The intial state is optional. If not specified, the state machine
starts in an unnamed initial state.
The following clauses constrain invocations to occur within specific
states and define how an invocation will change the current state of a
state machine.
when(state-machine.is("state-name")); |
Constrains the last expectation to occur only when the state
machine is in the named state. |
then(state-machine.is("state-name")); |
Changes the state of state-machine to the named state
when the invocation occurs. |
For example:
final States pen = context.states("pen").startsAs("up");
one (turtle).penDown(); then(pen.is("down"));
one (turtle).forward(10); when(pen.is("down"));
one (turtle).turn(90); when(pen.is("down"));
one (turtle).forward(10); when(pen.is("down"));
one (turtle).penUp(); then(pen.is("up"));
例子代码为:
package nl.enovation.commons.monitor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Collection;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Flags.Flag;
import javax.mail.internet.MimeMessage;
import nl.enovation.commons.IdGenerator;
import nl.enovation.commons.MailIdGenerator;
import nl.enovation.ems.commons.mailsender.MailSender;
import nl.enovation.ems.commons.mailsender.SMTPMailSender;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
public class MailLoopMonitorTest {
private Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
@Test
public void shouldReturnOkStatus()throws Exception{
//set up
final MailSender mailSender = context.mock(MailSender.class);
final MailChecker mailChecker = context.mock(MailChecker.class);
final String name="MailLoopMonitor";
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final Status OkStatus=new Status(Status.Code.OK,name,"Ok");
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String subject="TestSubject";
final String message="TestBody";
// expectations
context.checking(new Expectations() {
{
allowing (mailChecker).check(with(any(String.class)));
will(returnValue(OkStatus));
one(mailSender).sendMessage(with(any(MimeMessage.class)),with(any(String.class)),with(any(Collection.class)));
}
});
//execute
mailLoopMonitor.setIdGenerator(new MailIdGenerator());
mailLoopMonitor.setMailChecker(mailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
assertEquals(Status.Code.OK, (mailLoopMonitor.check(name)).getCode());
// verify
context.assertIsSatisfied();
}
@Test
public void shouldReturnErrorStatusOnSendFailure() throws Exception{
// set up
final MailChecker mailChecker = context.mock(MailChecker.class);
final String name="MailLoopMonitor";
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final IdGenerator idGenerator=new MailIdGenerator();
final SMTPMailSender mailSender=new SMTPMailSender();
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String subject="TestSubject";
final String message="Test";
final String host="wrong smtp host";//set a wrong host,should cause connect failure.
final int checkTimes=3,intervalTime=1000;
// expectations
context.checking(new Expectations() {
{
//should not execute MailCheck.check();
}
});
// execute
mailSender.setHost(host);
mailSender.setPort(25);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setCheckTimes(checkTimes);
mailLoopMonitor.setIntervalTime(intervalTime);
mailLoopMonitor.setMailChecker(mailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setIdGenerator(idGenerator);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
Status status=mailLoopMonitor.check(name);
assertEquals(Status.Code.CRITICAL, status.getCode());
assertNotNull(status.getCause());
assertNotNull(status.getMessage());
// verify
context.assertIsSatisfied();
}
@Test
public void shouldReturnErrorStatusOnConnectedFailure()throws Exception{
//set up
final MailSender mailSender = context.mock(MailSender.class);
final IdGenerator idGenerator=context.mock(MailIdGenerator.class);
final String name="MailLoopMonitor";
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String receiver="yves";
final String subject="TestSubject";
final String message="TestBody";
final String host="wrong host"; //a wrong pop3 host
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final Pop3MailChecker pop3MailChecker=new Pop3MailChecker();
// expectations
context.checking(new Expectations() {
{
allowing(idGenerator).getNextId();
will(returnValue("2007-12-337 13:12:3750"));
one(mailSender).sendMessage(with(any(MimeMessage.class)),with(any(String.class)),with(any(Collection.class)));
}
});
//execute
Properties props = new Properties();
props.put("mail.pop3.host",host);
Session session = Session.getDefaultInstance(props, null);
pop3MailChecker.setStore(session.getStore("pop3"));
pop3MailChecker.setHost(host);
pop3MailChecker.setPort(110);
pop3MailChecker.setUser(receiver);
pop3MailChecker.setPassword("");
mailLoopMonitor.setIdGenerator(idGenerator);
mailLoopMonitor.setMailChecker(pop3MailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
Status status =mailLoopMonitor.check(name);
assertEquals(Status.Code.CRITICAL, status.getCode());
assertNotNull(status.getCause());
assertNotNull(status.getMessage());
// verify
context.assertIsSatisfied();
}
@Test
public void shouldReturnErrorStatusOnGetMessagesFromFolderFailure()throws Exception{
//set up
final MailSender mailSender = context.mock(MailSender.class);
final Store store=context.mock(Store.class);
final Folder folder=context.mock(Folder.class);
final String name="MailLoopMonitor";
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String subject="TestSubject";
final String message="TestBody";
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final Pop3MailChecker pop3MailChecker=new Pop3MailChecker();
// expectations
context.checking(new Expectations() {
{
one(mailSender).sendMessage(with(any(MimeMessage.class)),with(any(String.class)),with(any(Collection.class)));
allowing(store).connect(with(any(String.class)),with(any(String.class)),with(any(String.class)));
allowing(store).isConnected();will(returnValue(false));
allowing(store).close();
allowing(folder).open(Folder.READ_WRITE);
allowing(folder).close(true);
allowing(folder).isOpen();will(returnValue(false));
allowing(folder).getMessages();will(throwException(new MessagingException()));
}
});
//execute
pop3MailChecker.setStore(store);
pop3MailChecker.setFolder(folder);
mailLoopMonitor.setIdGenerator(new MailIdGenerator());
mailLoopMonitor.setMailChecker(pop3MailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
Status status =mailLoopMonitor.check(name);
assertEquals(Status.Code.CRITICAL, status.getCode());
assertNotNull(status.getCause());
assertNotNull(status.getMessage());
// verify
context.assertIsSatisfied();
}
@Test
public void shouldReturnNoMatchedMessageIDsOnMacthID()throws Exception{
//set up
final MailSender mailSender = context.mock(MailSender.class);
final IdGenerator idGenerator=context.mock(MailIdGenerator.class);
final Store store=context.mock(Store.class);
final Folder folder=context.mock(Folder.class);
final Message mockedMessage=context.mock(Message.class);
final String name="MailLoopMonitor";
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String subject="TestSubject";
final String message="TestBody";
final Message[] messages={mockedMessage};
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final Pop3MailChecker pop3MailChecker=new Pop3MailChecker();
final String generatedID="2007-12-337 13:12:3750";
final String[] headInformation = {"not the same as generatedID"};//not the same as generatedID,
//should return No matched message IDs
// expectations
context.checking(new Expectations() {
{
allowing(idGenerator).getNextId();will(returnValue(generatedID));
one(mailSender).sendMessage(with(any(MimeMessage.class)),with(any(String.class)),with(any(Collection.class)));
allowing(store).connect(with(any(String.class)),with(any(String.class)),with(any(String.class)));
allowing(store).isConnected();will(returnValue(false));
allowing(store).close();
allowing(folder).open(Folder.READ_WRITE);
allowing(folder).close(true);
allowing(folder).isOpen();will(returnValue(false));
allowing(folder).getMessages();will(returnValue(messages));
allowing(mockedMessage).getHeader("Generated-ID");will(returnValue(headInformation));
allowing(mockedMessage).setFlag(Flag.DELETED, true);
}
});
//execute
pop3MailChecker.setStore(store);
pop3MailChecker.setFolder(folder);
mailLoopMonitor.setIdGenerator(idGenerator);
mailLoopMonitor.setMailChecker(pop3MailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
Status status =mailLoopMonitor.check(name);
assertEquals(Status.Code.CRITICAL, status.getCode());
assertNotNull(status.getCause());
assertNotNull(status.getMessage());
// verify
context.assertIsSatisfied();
}
@Test
public void shouldReturnErrorStatusOnOpenFolderFailure()throws Exception{
//set up
final MailSender mailSender = context.mock(MailSender.class);
final Store store=context.mock(Store.class);
final Folder folder=context.mock(Folder.class);
final String name="MailLoopMonitor";
final String recipient="yves.guo@localhost.com";
final String sender="yves.guo@vanad.com.cn";
final String subject="TestSubject";
final String message="TestBody";
final MailLoopMonitor mailLoopMonitor = new MailLoopMonitor();
final Pop3MailChecker pop3MailChecker=new Pop3MailChecker();
// expectations
context.checking(new Expectations() {
{
one(mailSender).sendMessage(with(any(MimeMessage.class)),with(any(String.class)),with(any(Collection.class)));
allowing(store).connect(with(any(String.class)),with(any(String.class)),with(any(String.class)));
allowing(store).isConnected();will(returnValue(false));
allowing(store).close();
allowing(folder).open(Folder.READ_WRITE);will(throwException(new MessagingException()));
allowing(folder).close(true);
allowing(folder).isOpen();will(returnValue(false));
}
});
//execute
pop3MailChecker.setStore(store);
pop3MailChecker.setFolder(folder);
mailLoopMonitor.setIdGenerator(new MailIdGenerator());
mailLoopMonitor.setMailChecker(pop3MailChecker);
mailLoopMonitor.setMailSender(mailSender);
mailLoopMonitor.setSender(sender);
mailLoopMonitor.setRecipient(recipient);
mailLoopMonitor.setSubject(subject);
mailLoopMonitor.setMessage(message);
Status status =mailLoopMonitor.check(name);
assertEquals(Status.Code.CRITICAL, status.getCode());
assertNotNull(status.getCause());
assertNotNull(status.getMessage());
// verify
context.assertIsSatisfied();
}
}