Thursday, April 29, 2010

Unit testing with EasyMock and Spring

Let's come up with simple test scenario. I have two Spring Service implementations with corresponding interfaces. I like to test only the process method in FooServiceImp class in isolation.

The problem is FooServiceImp.peocess() calls FooBarService.process() method which I don't want to test with this peticular Unit test.

So how do I test just FooServiceImp.process() method without testing FooBarService. EasyMock is to rescue. Before the testing I can replace the reference of FooBarService to a EasyMock object and add certain expected behavior to the Mock.

Behaviors I like to add to mock:
1. The return value of the Mock service.
2. Capture the parameters passed to the Mock
Service.
3. Add a Exception to test Exception scenario

Here are the codes. Happy Unit Testing:

FooServiceTest.java

import static org.junit.Assert.*; 
import java.util.List; 
import org.easymock.Capture; 
import org.easymock.EasyMock; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 


@ContextConfiguration(locations = { "classpath:Foo.xml" }) 
@RunWith(SpringJUnit4ClassRunner.class) 
public class FooServiceTest 
{ 
//Get a reference of FooServiceImp from Spring contexts 
@Autowired 
FooServiceImp fooService; 


@Before 
public void setUp() 
throws Exception 
{ 
//Replace with EasyMock object 
fooService.fooBarService = EasyMock.createNiceMock(FooBarService.class); 

} 

/* 
* Test the regular scenario 
*/ 
@Test 
public void testFooService() throws Exception{ 

//Creating a input value of String array 
String[] values = {"value1","value2", "value3"}; 

//Create Easymock Capture type to capture the 
Capture<List<String>> captureValues = new Capture<List<String>>(); 

//Setting behavior for the Mock service. 
EasyMock.expect(fooService.fooBarService.process( EasyMock.capture(captureValues))).andReturn("value1"); 

EasyMock.replay(fooService.fooBarService); 

//Test the return value 
assertEquals(fooService.process(values),"value1"); 

EasyMock.verify(fooService.fooBarService); 

//Check the parameters on Mock service call 
assertEquals(captureValues.getValue().get(0),"value1"); 


} 

/* 
* Test the Exception scenario 
*/ 
@Test(expected = Exception.class) 
public void testFooServiceException() throws Exception{ 

EasyMock.reset(fooService.fooBarService); 

String[] values = {"value1","value2", "value3"}; 

Capture<List<String>> captureValues = new Capture<List<String>>();  

//Setting the expected Exception on the Mock service 
EasyMock.expect(fooService.fooBarService.process( EasyMock.capture(captureValues))).andThrow(new Exception()); 

EasyMock.replay(fooService.fooBarService); 

//Should get an exception to pass the test 
fooService.process(values); 


} 
} 


FooService.java

public interface FooService 
{ 
public String process(String[] values) throws Exception; 
} 

FooServiceImp.java
import java.util.Arrays; 
import java.util.List; 
import org.springframework.beans.factory.annotation.Autowired; 

public class FooServiceImp 
implements FooService 
{ 

@Autowired 
FooBarService fooBarService; 

@Override 
public String process(String[] values) throws Exception 
{ 
List<string> arrayList = Arrays.asList(values); 

System.out.println(arrayList); 
return fooBarService.process((List<string>)Arrays.asList(values)); 
} 
} 

FooBarService.java
import java.util.List; 

public interface FooBarService 
{ 
public String process(List<String> values) throws Exception; 
} 

FooBarServiceImp.java:
import java.util.List; 

public class FooBarServiceImp 
implements FooBarService 
{ 
@Override 
public String process(List<String> values) throws Exception 
{ 
System.out.println("In FooBar" + values.get(0)); 
return values.get(0); 
} 
} 

Foo.xml - Spring conf file
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:p="http://www.springframework.org/schema/p" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:jms="http://www.springframework.org/schema/jms" 
xmlns:amq="http://activemq.apache.org/schema/core" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd 
http://www.springframework.org/schema/jms 
http://www.springframework.org/schema/jms/spring-jms.xsd 
http://activemq.apache.org/schema/core 
http://activemq.apache.org/schema/core/activemq-core.xsd"> 

<bean id="fooService" primary="true" 
class="com.icrossing.service.FooServiceImp"> 
</bean> 

<bean id="fooBarService" primary="true" 
class="com.icrossing.service.FooBarServiceImp"> 
</bean> 

</beans> 

No comments:

Post a Comment