Tutorial: JUnit-Rule

Hi,

as announced, here is the short tutorial for writing your own JUnit-Rule.

JUnit provides with the @Rule and @ClassRule annotations a powerful way to reduce the boilerplate code for setting up structures / assumptions. It also removes, in the most cases, the requirement of using inheritance and replaces it with composition.

Short overview

A @ClassRule annotated rule is invoked only once, like the @BeforeClass method. A @Rule annotated rule is invoked before every testmethod invocation, like the @Before method.

Every rule must implement the TestRule interface. This interface provides the method

TestRule.apply(Statement base, Description description) : Statement

which is called from the JUnit framework and where we can implement our own logic.

The statement is the current execute testmethod, the description provides information about a atomic (single) test or a compound (has child tests) test. This are, for example, annotations or method name.

The returned statement can be the same as the base or a new one which wraps the base statement or replaces it.

The idea

In this tutorial I chose a rule which is used as a @Rule annotated rule.
I decided to implement a rule which sets up and tears down a file/directory structure. I know that there is an existing rule, which provides the equal functionality. But I like the idea of using annotations as a domain specific language. And I also try to minimize the code in the @Before method an my test-method.

  1. Using 2 types of annotations:
    • A File annotation which specifies files for every directory
    • A Directory annotation which specifies a directory and a set of files for it
  2. Both annotations should be used at class and method level
    • A annotation at class level is used for every single testmethod
    • A annotation at method level is only used for this specific testmethod
  3. Returning a new statement which wraps the original one.
    This wrapper sets up the structure before executing the original statement and tears it down after that.

What to do

  1. Create a FileSetup annotation. This annotation takes a string array of filenames.
  2. Create a DirectorySetup annotation. This annotation takes a directory name and a string array of filenames.
  3. Create a AnnotationParser which takes the description and the testclass.
    It provides a method which returns a HashMap<string, hashset<string=””>>. This map represents the directory and file structure to be setted up for the current test method.
  4. Create a FilePrepareStatement which takes the HashMap and generates / destroys the required structure before invoking the original test.

Let’s get ready to rumble

I skip the implementation of the FileAnnotationParser and both annotations. You can find the complete sourcecode on my github site.

From my point of view the most interesting things are the FilePrepareStatement and the FilePrepareRule.

So let’s start with the FilePrepareRule.
It’s quiet simple, it must, as stated out previously, implement TestRule, using the FileAnnotationParser and return a FilePrepareStatement.
To read the annotations at classlevel it must also have knowledge of the testclass.

 
public class FilePrepareRule implements TestRule {

	private final Object classUnderTest;
	FileAnnotationParser fileAnnotationParser;
	public FilePrepareRule(Object classUnderTest) {
		this.classUnderTest = classUnderTest;
		fileAnnotationParser = new FileAnnotationParser();
	}

	@Override
	public Statement apply(Statement base, Description description) {
		fileAnnotationParser.setDescription(description);
		fileAnnotationParser.target = classUnderTest;
		final HashMap<string, hashset<string="">> structure = fileAnnotationParser.parseAnnotationsAndCreateStructure();
		return new FilePrepareStatement(base, structure) ;
	}
}

As you may notice, it’s a lean and clean code.

Now let’s continue with the FilePrepareStatement.
The statement wraps the original statement and creates / destroys the structure provided by the HashMap generated by the FileAnnotationParser.

 
public class FilePrepareStatement extends Statement {

	private Statement base;
	private HashMap<string, hashset<string="">> directoryStructure;

	public FilePrepareStatement(final Statement base, HashMap<string, final="" hashset<string="">> directoryStructure) {
		this.base = base;
		this.directoryStructure = directoryStructure;
	}

	@Override
	public void evaluate() throws Throwable {
		try {
			setupStructure();
			base.evaluate();
		} finally {
			tearDownStructure();
		}
	}
}

I skip also the implementation of setUpStructure and tearDown, because it’s not so interesting at this point.

How to use

Let’s assume that you have a class which requires some files in a specific directory structure.

Usually you have to do something like this:

 
public class FunnyTest {
	@Before
	public void setUp() {
		File directory = new File("./tmp/funnyTest");
		if(!directory.exists)
		directory.create();
		//And so on
	}
	@After
	public void tearDown() {
		File directory = new File("./tmp/funnyTest");
		if(!directory.exists)
		directory.delete();
		//And so on
	}
}

This works well if you didn’t have a testmethod specific setup. In this case you must do this in your testmethod, which means you pollute them with, IMHO, unnecessary code. This has also an impact on the readability and increases the chance of errors. For example forgotten clean up or something else.

With this rule the same problem looks like this:

 
@DirectorySetup(directory="./tmp/funnyTest", files={"a.txt", "b.txt"})
public class FunnyTest {
	@Rule
	public FilePrepareRule filePrepareRule = new FilePrepareRule(this);
	@Before
	public void setUp() {
		//Do other stuff
	}
	@After
	public void tearDown() {
		//Do other stuff
	}
}

As you see, it’s much cleaner and easier to read.

If a single testmethod needs a special directory setup and a additional file in the common directory, you can do it like this

 
@Test
@DirectorySetup(directory="./tmp/specialSetup", files={"s1.txt", "s2.txt"})
@FileSetup(files={"s3.txt"})
public void someMethod() { .... }

I hope I could provide you a short overview of how to use the rule annotations of JUnit.

Cheers

Peter

 
2 Kudos
Don't
move!

One thought on “Tutorial: JUnit-Rule


Leave a Reply

Your email address will not be published. Required fields are marked *