Thursday, September 20, 2012

How to use Junit Rule -- A powerful Junit feature

Junit has a powerful feature  called@Rule annotation.

The purpose of the @Rule annotation is to mark public fields of a test class. These fields must be of type TestRule(new API, previous one is MethodRule), or an implementing class. Such TestRule behave similar to a AOP aspects, of course without use of any AOP library and specialized for Tests. They can execute code before, after or instead of a test method. Example use cases listed in the release notes include:


  • Notification on tests
  • Setting up or tearing down resources, especially when they are used in multiple test classes
  • Special checks performed after every test, possibly causing a test to fail.
  • Making information about the test available inside the test

Let's take a look at how to use it:

Assuming we have to a bunch of test cases, each has a name with a pattern like test[Method]By[People], the suffix is the author who write this test case. But we want to execute a group of test cases by the same people. But we don't want to change comment the unnecessary code.
Therefore, we have a choice of using the Junit Rule to implement this requirement.

First we create a class to implement the Interface TestRule. The constructor takes the author name that we want to test. The method apply() use java regular expression Pattern to do the filter. If we find the author name appear at the end of method name to be tested, just leave it as before. Otherwise, we create an anonymous class implementing abstract class Statement.

See the code:
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.AssertionFailedError;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class MethodMatcherRule implements TestRule{
    
 private String author;
 MethodMatcherRule(String author){
     this.author=author;
    }
 @Override
 public Statement apply(Statement base, Description description) {
  // TODO Auto-generated method stub
  Pattern pattern=Pattern.compile(author+"$",Pattern.CASE_INSENSITIVE);
  Matcher matcher=pattern.matcher(description.getMethodName());
  if(matcher.find()){      
     return base; 
  }
  return new Statement(){             
   @Override
   public void evaluate() throws Throwable {
    // TODO Auto-generated method stub
    throw new AssertionFailedError("Sorry we don't test this method");
       
   }
   
   
  };
 }

}

How to use this rule is really simple.
Step one: Create a public attribute of your rule.
Step two: Instantiate the field you just create.
Step three: Write an annotation @Rule before you field.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Rule;
import org.junit.Test;

public class SimpleTestCases {

 @Rule
 public MethodMatcherRule rule=new MethodMatcherRule("Tom");
 

 @Test
 public void testAByTom() {
  assertTrue(true);

 }

 @Test
 public void testBByTom() {
  assertFalse(false);
 }

 @Test
 public void testByABC(){
  assertEquals(new Integer(11),new Integer(11)); 
 }
 
 @Test
 public void testAByJim() {
  assertFalse(false);

 }
}



No comments:

Post a Comment