Friday, September 21, 2012

Getting Started with jMock

Most people are familiar with using JUnit. When you use JUnit, you're really verifying the state of the object being tested. For example, suppose you were creating a program that would tell people's fortunes. We'll use the following interface for our API.
interface Psychic {
   public String getFortune( );
   public void pay(double amount);
}
Since using a real Psychic for our tests would be extremely expensive (both time and money wise), let's create what is known as a test double - something to stand in for the real thing. In particular, we'll be using what Martin Fowler calls a Stub. According to Fowler: “Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.”
import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;

public class PsychicTest {

   @Test
   public void testFortuneMethod( ) {
      Psychic stub = new Psychic() {
         public void pay(double amount) {
         }

         public String getFortune( ) {
            return "You will have a long and happy life.";
         }
      };

      assertThat(psychic.getFortune( ), is(notNullValue()));
   }
}
Notice that no matter how much we pay the psychic, it's always going to return the same result. (“You will have a long life and happiness.”) We're also not verifying that the getFortune( ) method gets called. We could have just as easily done the following and the test would have still passed.
assertThat("You will have a long life and happiness.", is(not(null));
If you need to verify the behavior of an object then you'll need to use what Fowler calls Mock objects. “Mocks are...objects pre-programmed with expectations which form a specification of the calls they are expected to receive” (Martin Fowler) This where jMock comes in.
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;

// Note: I'm using JMock 2.6.0

@RunWith(JMock.class)
public class PsychicTest {

   private Mockery context = new JUnit4Mockery();

      @Test
      public void testFortuneMethod( ) {
         final Psychic mock = context.mock(Psychic.class);

         context.checking(new Expectations() {
            {
               oneOf(mock).getFortune();
               will(returnValue("You will have a long life and happiness."));
            }
         });

         assertThat(mock.getFortune( ), is(notNullValue()));
         context.assertIsSatisfied();
   }
}
Note: jMock can only mock interfaces, not classes. This follows good programming practice in that you should program to an interface, not an implementation.

References

No comments:

Post a Comment