James Carr's complete blog can be found at: http://blog.james-carr.org

Items:   1 to 5 of 12   Next »

Wednesday, April 21, 2010

In my previous post I went over some of the new annotations and annotation features available in Mockito with the 1.8.3 release and promised the following day I would post details on the @Spy annotation. Well, unfortunately practically a month has passed since then! But fear not, here is the overview of the @Spy annotation that I promised. :)

Using It

Using the @Spy annotation is quite easy… just use the MockitoJunit4Runner and annotate a real object instance as follows:

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;

@RunWith(MockitoJUnitRunner.class)
public class SpyExample {
	@Spy
	private List>String< list = new ArrayList>String<();

}

This will essentially proxy the real ArrayList implementation with a CGLib proxy. No big deal there. Let’s try stubbing a call so that it returns “hey” if we request index 32:

        @Test
	public void spyExample(){
		when(list.get(32)).thenReturn("hey");

		assertThat(list.get(32), equalTo("hey"));
	}

This blows up in our face with a big hairy IndexOutOfBoundsException while trying to stub the method call out. Duh… it’s calling the real implementation! Therefore we opt to use doReturn(...) instead as it allows us to stub the method we want without calling the real thing.

@RunWith(MockitoJUnitRunner.class)
public class SpyExample {
	@Spy
	private List>String< list = new ArrayList>String<();
	@Test
	public void spyExample(){
		doReturn("hey").when(list).get(32);

		assertThat(list.get(32), equalTo("hey"));
	}
}

If we rerun the example we will now see green bar rather than IndexOutOfBoundsException. We can still call real methods like add, remove, size, etc and they’ll still behave as expected; only when get(32) is called will it stub the call and return the canned value.

For the most part, I’m not a fan of this technique. In the real world I’d either choose between using the real thing (which I would most certainly ALWAYS do if it were a collection or some other base type) or prefer a wholesome complete stub using @Mock (but adhering to the rule that you only mock/stub objects you actually own).

Sadly there are always weird cases where you need to do it to make something easier to test… a good example was some legacy code I was dealing with where the test interacted with a real object to manipulate graphics in the UI. Those interactions needed to remain intact in order for the existing tests to pass, however one method caused a web service call that made the tests brittle and unpredictable. Spying that object and stubbing out the method that made the web service call was the best way to exercise the different scenarios around what that method returned. “Fixing” the collaborator’s API itself or isolating it from the class under test is a whole separate issue. :)

Verifying Messages Between Collaborators

A really useful feature I find in using spies is the ability to verify messages passed to a real collaborator in a big ball of mud type of system. Imagine this scenario: you pick up a story that is to just modify some logic in how messages are passed from one object to another. Sounds simple, until you look at the existing unit test:

public class CustomerHandlerBOTest {
	private ComplexClassThatDoesAlot formulator = DumbStaticFactory.create();
	private CustomerHandlerBO bo = new CustomerHandlerBO(formulator);

	....
}

The class names and that static factory are bad enough, but let’s imagine the code has gotten itself to the situation where simply providing complete test double for ComplexClassThatDoesAlot will force you to introduce test doubles for 20 other objects. Or perhaps the method is large and the default behavior of the collaborator allows you to execute the code that needs to execute to get you to where you want. You can simply write a verification of the argument passed to it and then make the modification you need to.

@RunWith(MockitoJUnitRunner.class)
public class CustomerHandlerBOTest {
	@Spy
	private ComplexClassThatDoesAlot formulator = DumbStaticFactory.create();
	@Captor
	private ArgumentCaptor>Item< arg;
	private CustomerHandlerBO bo = new CustomerHandlerBO(formulator);

	@Test
	public void shouldPassItemRequestWithExpectedCalculation(){
		...
		bo.calculate(BULK_ORDER);
		verify(formulator).audit(arg.capture());

		Item actualItemAudited = arg.getValue();
		assertThat(actualItemAudited.getPrice(), equalTo(EXPECTED_CALCULATED_PRICE));
	}

Of course, once the tests pass I might start applying some obvious refactorings starting as renaming away from those hideous names and either eventually get to the point where a @Spy is no longer needed or at least have that piece under test. The good thing about this is you can verify the interaction between these two objects without having to crack open encapsulation through a backdoor default or protected method (something I’ve been guilty of).

You Can Stub Internal Calls

I’ve seen situations where someone was dealing with legacy code and wanted to do this:

doReturn(1).when(bookingSystem).getActiveReservations();

bookingSystem.processReservations();

Where processReservations() makes a call to getActiveReservations() internally. Although this clearly hints at a hidden collaborator that needs to exist or break free from bookingSystem, Mockito apparently will let you get away with this. I seriously wouldn’t suggest it when test driving fresh code, again but there’s always those situations where you have no options.

Hope that is useful for you… overall I find the @Spy annotation to simply be a useful tool for fighting legacy code but should be used with care when specifying fresh code, if at all.


Tuesday, March 9, 2010

It’s only been a few days since the Mockito 1.8.3 release and I have to say that for a minor release there’s a lot I like about it!

What I like about the new annotations is the ability to have test cases that are completely free of @Before. Observe this example of a class that takes a text input string, translates it to an AuctionEvent, and fires it off to an AuctionEventListener:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();

	@Test
	public void shouldSendAnEventToTheListener(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(any(AuctionEvent.class));
	}
}

This is a first step, to just verify an event is passed to the listener (we don’t care about it’s contents yet). @Mock creates a mock on each test method run, and @InjectMocks will pass mocks to any matching setters or constructors.

Now I’ll implement a little code to make the example pass.

public class AuctionMessageTranslator {
	private AuctionEventListener listener;
	public void setListener(AuctionEventListener listener) {
		this.listener = listener;
	}

	public void sendMessage(String message) {
		listener.handleEvent(new AuctionEvent());
	}
}

Doesn’t do much… so let’s add a new example that verifies the contents of the message sent to the listener. Since this object is created by the translator (translating a string to an object) we’ll use an argument captor to capture and verify it’s value.

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Captor ArgumentCaptor<AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();

	@Test
	public void shouldSendAnEventToTheListener(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(any(AuctionEvent.class));
	}

	@Test
	public void shouldSendAnEventWithNamePrice(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(arg.capture());

		assertThat(arg.getValue().getName(), equalTo("PRICE"));
	}
}

It fails, so we implement the code to make it pass:

	public void sendMessage(String message) {
		listener.handleEvent(parseEvent(message));
	}

	private AuctionEvent parseEvent(String message) {
		AuctionEvent auctionEvent = new AuctionEvent();
		auctionEvent.setName(message.split(";")[1].split(":")[1].trim());
		return auctionEvent;
	}

This passes as the argument passed to the listener does indeed contain the event name. This is a little ugly, so let’s refactor it a little bit with our test providing a nice safety net:

	public void sendMessage(String message) {
		listener.handleEvent(parseEvent(message));
	}

	private AuctionEvent parseEvent(String message) {
		AuctionEvent auctionEvent = new AuctionEvent();
		auctionEvent.setName(unpackMessage(message).get("Event"));
		return auctionEvent;
	}

	private Map<String, String> unpackMessage(String message) {
		Map<String, String> pairs = new HashMap<String, String>();
		for(String pairString : message.split(";")){
			String[] pair = pairString.split(":");
			pairs.put(pair[0].trim(), pair[1].trim());
		}
		return pairs;
	}

Looks good, and the test case for it is pretty clean although it has a lot of annotations. We could change the injection strategy to use constructor injection since we don’t really want the object to even exist without a listener:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Captor ArgumentCaptor<AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator(listener);
...
}

As long as the @InjectMocks annotation is present, the MockitoJunitRunner will initialize the mocks do they’re available for injection. Drop the @InjectMocks annotation off, and it fails with a null pointer exception.

One interesting thing of note when using @InjectMocks with setter injection is if you do something silly like the following:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Mock AuctionEventListener listener2;
	@Captor ArgumentCaptor>AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();
	...
}

It will inject the first @Mock, not the second. Verifications against listener will work, while verifications against listener2 will fail as it was never injected.

Tomorrow I’ll include some examples of using the @Spy annotation as well as the different answer types you can configure @Mock annotated mocks with as of 1.8.3. ;)


Tuesday, March 9, 2010

I’m currently in chapter 12 of Growing Object Oriented Software Guided By Tests and thought I’d share another good tidbit from one of the asides:

Put Tests in a Different Package

We’ve adopted a habit of putting tests in a different package from the code they’re exercising. We want to make sure we’re driving the code through its public interfaces, like any other client, rather than opening up a package-scoped back door for testing.

Good point! Almost everytime I’ve found my self expose a method that should be private as protected or default it’s been because that method was really in gross violation of Single Responsibility Principle and I’ve often taken such code and extracted it to a separate object.

Thoughts?


Sunday, March 7, 2010

Szczepan has announced on the Mockito user mailing list that 1.8.3 of Mockito has been released. This released includes several small (but useful) additions as well as bug fixes.

The two parts of this release I like are the new annotations @Spy, @Captor, and @injectMocks. These add to the already useful @Mock annotation to simplify test setup tremendously. Additionally, the @Mock annotation is now configurable so you can add different Mock/Stub styles; previously @Mock only supported the default mock behavior, now you can configure it to RETURNS_MOCKS, CALLS_REAL_METHODS, etc.

This release also includes a feature I requested that can be useful when trying to get legacy code under test, something I call deep stubs. Ever been in the situation where you have code with something like this in the middle of it:

someCollaborator.getFoo().doBarThings().getBaz().execute().processResult();

Normally to stub this call, you’ll have to mock every object returned by each method call, and then stub the last one. Examples for code like this can be a little verbose, but now you can just @Mock the aggregate root and do something like:

given(someColllaborator.getFoo().doBarThings().getBaz().execute().processResult).willReturn(resultObject);

Just remember friends, this is only good for legacy code… if you are already writing your code exemplar first to drive your design, you should know better than to make your object know too many details about it’s neighbors. :)

This release also includes a patch I submitted to stop having all of the examples in a test run when trying to run a single one in eclipse and intelliJ when using MockitoJunitRunner.

For a complete list of features/fixes, see the release notes.


Monday, March 1, 2010

I’ll be giving a presentation at Lambda Lounge this Thursday on Behavior Driven Development With Jspec. If you’re in the St.Louis area come on by and learn about BDD and how you can use it to drive your design. I’ll be using jspec to demonstrate how to build a functioning feature for a javascript library driven by small, iterative examples.


Items:   1 to 5 of 12   Next »