/** * */ package ch.fhzh.jmock.gettingstarted; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import junit.framework.Assert; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.Sequence; import org.jmock.integration.junit4.JMock; import org.jmock.integration.junit4.JUnit4Mockery; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** * Demonstration of some fuctions of the jmock library. * <p> * Have a look at * <a href="http://www.jmock.org/cookbook.html"> the * jmock cookbook</a> for a lot more examples. * </p> * * @author bseelige * */ @RunWith(JMock.class) public class PublisherTest { /** jmock context */ Mockery context; /** instance under test */ IPublisher instance; @Before public void setUp() { context = new JUnit4Mockery(); instance = new PublisherImpl(); } /** * In an outline, a jmock 2 test looks like * the following. */ @Test public void jmockTemplate() { // set up // ... set up code ... // define expectations context.checking(new Expectations() {{ // ... expectations go here ... // An expectations block can contain any number // of expectations. Each expectation has 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. }}); // execute // ... code being tested ... // after execution junit assertations // ... assertions ... } /** * Creates a simple mock for the ISubscriber interface. * <p> * After the code under test has finished our test must * verify that the mock Subscriber was called as expected. * If the expected calls were not made, the test will fail. * The MockObjectTestCase does this automatically. You * don't have to explicitly verify the mock objects * in your tests. The JMock test runner does this * automatically. You don't have to explicitly verify * the mock objects in your tests. * </p> * */ @Test public void oneSubscriberReceivesAMessage() { // set up final ISubscriber subscriber = context.mock(ISubscriber.class); instance.add(subscriber); final String message = "message"; // expectations // first bracet creates an anonymous inner class // second braced is the static initializer of the class context.checking(new Expectations() {{ // one and all the other invocation count methods // return an instance of the same class as it's // argument one (subscriber).receive("message"); }}); // execute instance.publish(message); } /** * Sequences are used to define expectations that 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. */ @Test public void oneSubscriberReceivesASequnce() { // set up final ISubscriber subscriber = context.mock(ISubscriber.class); instance.add(subscriber); final Sequence seq = context.sequence("msgseq"); // double braces again, see // http://www.c2.com/cgi/wiki?DoubleBraceInitialization final List<String> messages = new ArrayList<String>() {{ add("message1"); add("message2"); add("message3"); }}; // expectations // first bracet creates an anonymous inner class // second braced is the static initializer of the class context.checking(new Expectations() {{ // one and all the other invocation count methods // return an instance of the same class as it's // argument one (subscriber).receive(messages.get(0)); inSequence(seq); one (subscriber).receive(messages.get(1)); inSequence(seq); one (subscriber).receive(messages.get(2)); inSequence(seq); }}); // execute for (String message : messages) { instance.publish(message); } } /** * The "invocation count" of an expectation defines the * minimum and maximum number of times that the expected * method is allowed to be invoked. It is specified before * the mock object in the expectation. * * <ul> * <li>one: The invocation is expected once and once only.</li> * <li>exactly(n).of: The invocation is expected exactly n times.</li> * <li>atLeast(n).of: The invocation is expected at least n times.</li> * <li>atMost(n).of: The invocation is expected at most n times.</li> * <li>between(min, max).of: The invocation is expected at least min times and at most max times.</li> * <li>allowing: The invocation is allowed any number of times but does not have to happen.</li> * <li>ignoring: The same as allowing. Allowing or ignoring should be chosen to make the test code clearly express intent.</li> * <li>never: The invocation is not expected at all. This is used to make tests more explicit and so easier to understand.</li> * </ul> * */ @Test public void expectingMoreThanOnce() { // set up final ISubscriber subscriber = context.mock(ISubscriber.class); instance.add(subscriber); final String message = "message"; // expectations context.checking(new Expectations() {{ // one and all the other invocation count methods // return an instance of the same class as it's // argument exactly(10).of (subscriber).receive(message); //some alternatives: //allowing (subscriber).receive(message); //atLeast(5).of (subscriber).receive(message); //between(5, 15).of (subscriber).receive(message); //never (subscriber).receive(message); }}); // execute for (int i = 0; i<10 ; i++) { instance.publish(message); } } /** * Most of the time expectations specify literal parameter * values that are compared for equality against the * actual parameters of invoked methods (see above). * <p> * Sometimes, however, you will need to define looser * constraints over parameter values to clearly express * the intent of the test or to ignore parameters (or * parts of parameters) that are not relevant to the * behaviour being tested * </p> * <p> * Loose parameter constraints are defined by specifying * matchers for each parameter. Matchers are created by * factory methods, such as lessThan, equal and * stringContaining in the example above, to ensure * that the expectation is easy to read. The result * of each factory method must be wrapped by a call to * the with method. * </p> * <p> * Matchers can be combined to tighten or loosen the * specification if necessary. The set of matchers * is extensible1 so you can write new matchers to * cover unusual testing scenario. * </p> * @see<a href="http://www.jmock.org/custom-matchers.html"> * "http://www.jmock.org/custom-matchers.html"</a> */ @Test public void usingMatchers() { // set up final ISubscriber subscriber = context.mock(ISubscriber.class); instance.add(subscriber); final String message = "message"; // expectations context.checking(new Expectations() {{ // test for exact match one (subscriber).receive(message); // alternative test for exact match one (subscriber).receive(with(equal(message))); // test for object identity one (subscriber).receive(with(same(message))); // test for not null one (subscriber).receive(with(aNonNull(String.class))); // "ignoring" the parameter one (subscriber).receive(with(any(String.class))); // combined matcher one (subscriber).receive(with(not(aNull(String.class)))); // these are Hamcrest matchers, see // http://code.google.com/p/hamcrest/wiki/Tutorial // test for substring one (subscriber).receive(with(containsString("mess"))); }}); // execute for (int i=0; i<7; i++) { instance.publish(message); } } }