In this post I would like to show an example of how to use JUnit Rule to make testing easier.
Recently I inherited a rather complex system, which not everything is tested. And even the tested code is complex.
Mostly I see lack of test isolation.
(I will write a different blog about working with Legacy Code).
One of the test (and code) I am fixing actually tests several components together.
It also connect to the DB. It tests some logic and intersection between components.
When the code did not compile in a totally different location, the test could not run because it loaded all Spring context.
The structure was that before testing (any class) all Spring context was initiated.
The tests extend BaseTest, which loads all Spring context.
BaseTest also cleans the DB in the @After method.
Important note: This article is about changing tests, which are not structured entirely correct.
When creating new code and tests they should be isolated, testi one thing etc.
Better tests should use mock DB / dependencies etc.
After I fix the test and refactor, I’ll have confidence making more changes.
Back to our topic…
So, what I got is slow run of the test suit, no isolation and even problem running tests due to unrelated problems.
So I decided separating the context loading with DB connection and both of them from the cleaning up of the database.
In order to achieve that I did three things:
The first was to change inheritance of the test class.
It stopped inheriting BaseTest.
Instead it inherits AbstractJUnit4SpringContextTests
Now I can create my own context per test and not load everything.
Now I needed two rules, a @ClassRule and @Rule
@ClassRule will be responsible for DB connection
@Rule will cleanup the DB after / before each test
But first, what are JUnit Rules?
A short explanation would be that they provide a possibility to intercept test method, similar to AOP concept.
@Rule allows us to intercept method before and after the actual run of the method.
@ClassRule intercepts test class run.
A very known @Rule is JUnit’s TemporaryFolder.
(Similar to @Before, @After and @BeforeClass).
The easy part was to create a Rule that cleanup the DB before and after a test method.
You need to implement TestRule, which has one method: Statement apply(Statement base, Description description);
You can do a-lot with it.
I found out that usually I will have an inner class that extends Statement.
The rule I created did not create the DB connection, but got it in the constructor.
Here’s the full code:
ClassRule is actually also TestRule.
The only difference from Rule is how we use it in our test code.
I’ll show it below.
The challenge in creating this rule was that I wanted to use Spring context to get the correct connection.
Here’s the code:
(ExternalResource is TestRule)
(Did you see that I could make DbCleanupRule inherit ExternalResource?)
The last part is how we use the rules.
A @Rule must be public field.
A @ClassRule must be public static field.
And there it is:
Hope it helps.
I got some good remarks from Logan Mzz at DZone: http://java.dzone.com/articles/junit-rules#comment-125673
- Link to Junit Rules: https://github.com/junit-team/junit/wiki/Rules
- There’s ErrorCollector rule, which avoids annoying test-fail-fix cycles for a single test.
- And RuleChain, which described in the comment