1. Overview
In this tutorial, we'll look at the basic structure of a JUnit test rule.
2. TestRule Interface
The TestRule interface is the main interface we must implement to create a Junit test rule.
TestRule has only one method, apply. This method gets two parameters: a statement (Statement) and a description (Description).
Now, let's look at these parameters.
2.1. Base Statement
The base parameter represents the actual test method that we write.
To better understand how JUnit stores a test method as an object, we must examine the built-in implementations of Statement. Out of these, InvokeMethod can store the details of a test method and invoke it using reflection:
So, we're assuming that base in TestRule.apply is an instance of InvokeMethod. However, the base statement may not always be an instance of InvokeMethod since JUnit can also wrap it in other Statements. To further analyze, let's investigate the following snippet from BlockJUnit4ClassRunner:
It first creates an InvokeMethod instance. Then if there is an expected exception, it creates another statement. It then checks whether a timeout is set. In each phase, InvokeMethod gets wrapped in another statement. So our test rule gets the last created statement, not the original InvokeMethod instance.
Moreover, if we apply multiple test rules to a test, the result of one test rule will be the base statement of the next rule. We can see this behavior in the RunRules class:
2.2. Description
The description parameter provides information about the test. We can get the declared annotations, parent test class, and other information.
3. How to Handle Original Statement
Now let's look at how we can handle the original statement in our test rule.
The general idea is that we get the base statement and return a new statement. This new statement may be a wrapper around the original statement or it may be a brand new statement. Moreover, we can put checks before the original statement and skip the test if the checks fail.
Next, we'll investigate some built-in implementations.
3.1. Details of Verifier
The Verifier base class provides a template so that we can perform verification checks after the test completes.
Note that the returned statement is a wrapper around the original one.
3.2. Details of ExternalResource
Let's continue with ExternalResource.
It also provides a template so that we can open and close an external resource.
Similar to Verifier, ExternalResource returns a wrapper statement around the original one.
4. Summary
In this tutorial, we've looked at the internal structure of a JUnit test rule.
We can think of the TestRule implementations as decorators around our test code. After all test rules are applied, the final Statement can have different layers of functionality.