Tuesday, July 30, 2013

Junit multiple users functionalities with TestRule interface

Recently I was trying to do some performance testing integrated with build. There are some use cases that I want to simulate multi users(Threads) behavior inside my junit test cases to have multiple threads executing the tests concurrently.

Then TestRule, a built in Junit interface which brought my attention, is easy and powerful to customize your test code behaviors.

TestRule
As I understand, the TestRule is an interface which developer can inject into test classes, which is similar to the aspectj Around point cut.

The code below implements TestRule and return an inner class as CustomStatement that injects some code before and after the testing code (base.evaluate())

public class SimpleTestRule implements TestRule{
 
 public Statement apply(Statement base, Description description) {
  return new CustomStatement(base);
 }
 
 private class CustomStatement extends Statement{
  private Statement base;
  
  public CustomStatement(Statement base) {
   this.base = base;
  }

  @Override
  public void evaluate() throws Throwable {
   //Before test execution
   System.out.println("Before Test Running");
   //Test execution
   base.evaluate();
   //After test execution
   System.out.println("After Test Running");
   
  }
  
 }
}

Use the above Custom Test Rule
public class SimpleTestRuleTest {
 
 @Rule
 public TestRule rule = new SimpleTestRule();
 
 @Test
 public void test(){
  System.out.println("My test is running");
 }
}

Code execution Result:

Before Test Running
My test is running
After Test Running


Multiple User TestRule

After understanding the above simple test rule, we can start create a more complicate rule to support multiple threading of tests.

The following code wrapped the test execution code (base.evaluate()) into an independent thread.

public class MultiUserTestRule implements TestRule{
 
 private final int numOfUsers;
 private final ExecutorService executor;
 
 public MultiUserTestRule(int poolSize, int numOfUsers){
  this.numOfUsers = numOfUsers;
  executor = Executors.newFixedThreadPool(poolSize);
 }

 public Statement apply(Statement base, Description description) {
  return new CustomStatement(base);
 }
 
 private class CustomStatement extends Statement{
  
  private Statement base;
  
  public CustomStatement(Statement base) {
   this.base = base;
  }

  @Override
  public void evaluate() throws Throwable {
   for(int i=0; i< numOfUsers; i++){
    executor.execute(new Thread(new Runnable() {
     
     public void run() {
      try {
       base.evaluate();
      } catch (Throwable e) {
       throw new IllegalArgumentException(e);
      }
     }
    }));
   }
  }
  
 }

}
Use the above test Rule
public class MultiUserTestRuleTest {
 /**
  * The thread pool size is 10
  * There are 5 users run the tests
  */
 @Rule
 public TestRule rule = new MultiUserTestRule(10, 5);
 
 @Test
 public void test(){
  System.out.println(Thread.currentThread().getName() + " is running the test");
 }
}
The above test case is trying to print out each current thread name for demo purpose.
The result is:
pool-1-thread-1 is running the test
pool-1-thread-4 is running the test
pool-1-thread-5 is running the test
pool-1-thread-2 is running the test
pool-1-thread-3 is running the test

All the above code can be found here: https://github.com/datianshi/multiuser-junit

Tuesday, July 16, 2013

Spring MVC testing with session scoped beans

Spring MVC test is a new feature from Spring 3.2 that could help easy function/integration testing with the controller without bring up a real server. However, when controller autowiring  a session scoped bean and some controller request mapping methods have operations on the bean, how to make assertion on those operations becomes tricky. Here I will propose an approach to test against it.


Assume there is a session scoped bean configured as below:

@Component
@Scope(value= "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopeBean {
 
 private String value;

 public String getValue() {
  return value;
 }

 public void setValue(String value) {
  this.value = value;
 }
 
 
}
There is a controller autowired with the session scoped bean
@Controller
public class HomeController {
 
 @Autowired
 private SessionScopeBean sessionBean;
  
 @RequestMapping(value = "/sessionScope/{value}")
 public String test(@PathVariable String value){
  sessionBean.setValue(value);
  return "home";
 }
 
}
The above controller modify the value field of a session scope bean, then how would we retrieve that and assert the appropriate value in the session. Couple of tips here:


  • Spring saves the session scope bean as a session attribute "scopedTarget.${idOfTheBean}"
  • Spring mvc test provides request().sessionAttribute("attr", hamcrestMatcher) to assert the session
  • hamcrest matcher HasPropertyWithValue can be used to inject into the above expression


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:test-appContext.xml")
public class ControllerTest {
 
 private MockMvc mockMvc;
 
 @Autowired
 private WebApplicationContext wac;
 
 
 
 @Before
 public void setup(){
  this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
 }
 
 @Test
 public void test() throws Exception{
  mockMvc.perform(get("/sessionScope/abc")).
   andExpect(view().name("home")).
   andExpect(request().sessionAttribute("scopedTarget.sessionScopeBean", hasProperty("value", is("abc"))));
 }
}
The above tests shows how the session attribute being asserted

Cheers