JUnit for C# Developers 5 – Delving Further into Mocking
Today, I’m continuing my series on unit testsing with JUnit with a target audience of C# developers.
Goals
These are today’s goals that I’m going to document:
- See about an NCrunch-equivalent, continuous testing tool for Eclipse and Java
- Testing the various complexities of the @PathVariable annotations
- Use mockito to perform a callback
- Mocking something that’s not an interface
On to the Testing
The first goal is more reconnaissance than anything else. I have come to love using NCrunch (to the point where I may make a post or series of posts about it), and I’d love to see if there is a Java equivalent. NCrunch uses extra cores on your machine to continuously build and run your unit tests as you work. The result is feedback as you type as to whether or not your changes are breaking tests. The red-green-refactor cycle becomes that much speedier for it. My research led me to this stack overflow page, and two promising leads: infinitest and ct-eclipse (presumably for “continuous testing”). I’m pleased with those leads for now, and am going to shelve this as one of the goals in a future post. Today, I just wanted to investigate to see whether or not that was an option, and then move onto concrete testing tasks.
Next up, for Spring framework, my toggleLight method’s parameters need to be decorated with the @PathVariable attribute, which apparently allows delimited strings in the Request Mapping’s value to be mapped to parameters to the method. In this fashion, I’m able to map a post request REST-style URL to a request for toggling a light. To accomplish this, I studied up and wrote the following test:
@Test
public void has_parameters_decorated_with_PathVariable_annotation() throws NoSuchMethodException, SecurityException {
Class myClass = LightController.class;
Method myMethod = myClass.getMethod("toggleLight", String.class, String.class, String.class);
Annotation[][] myAnnotations = myMethod.getParameterAnnotations();
int myCount = 0;
for(int index = 0; index < myAnnotations.length; index++) {
if(myAnnotations[index][0] instanceof PathVariable)
myCount++;
}
assertEquals(3, myCount);
}
This failed, of course, and I was able to make it pass by updating my code to:
@RequestMapping(value="/{room}/{light}/{command}", method=RequestMethod.POST)
public void toggleLight(@PathVariable String room, @PathVariable String light, @PathVariable String command) {
_lightService.toggleLight(null, command.toLowerCase().equals("on"));
}
Note the @PathVariable annotations. I'm no expert here, but as I understand it, this takes variables in the mapping's value delimeted by {} and maps them to method parameters. In order to do this, however, the parameters need this annotation. So cool, I can keep doing TDD even as I add the boilerplate for Spring MVC.
At this point, however, I want to verify that the service is being invoked with parameters that actually correspond to toggleLight's arguments. Right now, we're just hardcoding null for the light. (Between last post and this one, I did some garden variety TDD using the Mockito verify() previously available in order to resolve the logic about passing true or false to the service for the light's value). Using verify(), I can make sure that I'm not passing a null light, but I have no means of actually inspecting the light. In the C#/Moq TDD world, to get to the next step, I would use the Moq .Callback(Action) functionality. In the Mockito/Java world, this is what I found:
@Test
public void calls_service_toggleLight_with_roomName_matching_room_parameter() {
LightManipulationService myService = mock(LightManipulationService.class);
LightController myController = buildTarget(myService);
String myRoom = "asdf";
myController.toggleLight(myRoom, "fdsa", "on");
ArgumentCaptor myLightArgument = ArgumentCaptor.forClass(Light.class);
verify(myService).toggleLight(myLightArgument.capture(), anyBoolean());
assertEquals(myRoom, myLightArgument.getValue().getRoomName());
}
I'm creating an ArgumentCaptor
@RequestMapping(value="/{room}/{light}/{command}", method=RequestMethod.POST)
public void toggleLight(@PathVariable String room, @PathVariable String light, @PathVariable String command) {
_lightService.toggleLight(new Light(room, light), command.toLowerCase().equals("on"));
}
I don't know that this counts as a callback, but it is the functionality I was looking for.
So, at this point, I'm temporarily done with the controller. Now, I want to implement the the service and have it make calls to Runtime.getRuntime.exec(). But, in order to do that, I need to be able to mock it. As you know, in C#, this is the end of the line for Moq. We can use it to mock interfaces and classes with virtual methods, but static methods and other test-killers require Moq's more powerful, heavyweight cousin: the isolation framework (e.g. Moles). So, I scurried off to see if Mockito would support this.
I did not have far to look. The Mockito FAQ offered the following as limitations of the tool: cannot mock final classes, cannot mock static methods, cannot mock final methods, cannot mock equals(), hashCode(). So, no dice there. We're going to need something else. And, almost immediately, I stumbled on PowerMock, billed as an extension to Mocktio. "PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more." You had me at "mocking of static methods."
So, I downloaded powermock-mockito.1.4.11-full.jar and slapped it in my externaljars directory along with Mockito. As it turns out, I needed more than just that, so I downloaded the full zip file from the site, which was in a file named "powermock-mockito-testng-1.4.11.zip". I ran into runtime errors without some of these supporting libraries. From here, I poked and prodded and experimented for a while. The documentation for these tools is not especially comprehensive, but I'm used to that in C# as well. This is what wound up working for me, as a test that my service was invoking the runtime's executable:
@RunWith(Enclosed.class)
public class LightManipulationServiceHeyuImplTest {
private static LightManipulationServiceHeyuImpl BuildTarget() {
return new LightManipulationServiceHeyuImpl();
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(Runtime.class)
public static class toggleLight {
/**
* Make sure the service is invoking the runtime's exec() to invoke heyu
* @throws IOException
*/
@Test(expected=IllegalArgumentException.class)
public void invokes_getRuntimes_exec() throws IOException {
LightManipulationServiceHeyuImpl myService = BuildTarget();
PowerMockito.mockStatic(Runtime.class); //We're going to set the mock's exec() up to throw an exception, and expect that exception
Runtime myMock = Mockito.mock(Runtime.class);
PowerMockito.doThrow(new IllegalArgumentException()).when(myMock).exec(Mockito.anyString());
PowerMockito.when(Runtime.getRuntime()).thenReturn(myMock);
myService.toggleLight(new Light("asdf", "Fdsa"), true);
}
...
In the first place, I'd forgotten how much I loathe java's checked exceptions, for all of the reasons I always did previously and now for a new one -- they're a headache with TDD. I mention this because I apparently need to have my test method throw that exception so that I can mock the runtime. (Not even use it -- mock it). The rest of the stuff in there, I learned by experimentation. You have to include some new annotations, and you have to setup PowerMockito to mock the static class. From there, I created a mock of what Runtime.getRuntime() returns (not surprisingly, it returns a Runtime). Then, I setup the mock Runtime to toss an exception when its exec method is called -- the one that I plan to use. I then expect this exception in the test. This is my clever (perhaps too clever) way of verifying that the exec() method is called in my class, without having tests that actually go issuing shell commands. That'd be a big bucket of fail, but I'd still like to test these classes and use TDD, so this is how it has to be.
Looking Ahead
Next time, I'll work my way through developing this service and document anything that comes up there. These mocking frameworks are new to me, so it's going to be a work in progress. I may or may not play with some of the continuous testing tools as well.
[…] DaedTech Professional Software Engineering Skip to content HomeServicesConsultingHome AutomationPortfolioProjectsDatabase TechnologiesMS AccessMySQLOraclePostgreSQLSQL ServerFrameworksContent Management System (CMS)Embedded SystemsRealtimeSpringWPFMarkup LanguagesCSSHTMLXAMLXMLOperating SystemsLinuxWindowsProgramming LanguagesAssemblyCC#C++JavaJavascriptPHPVBAVisual BasicTesting FrameworksJUnitMS TestNUnitVersion ControlClear CaseCVSGitSource SafeSubversionTFSPublicationsAdditional InformationAboutSocial MediaPrivacy PolicyBlog ← JUnit for C# Developers 5 – Delving Further into Mocking […]