I've experienced similar problems there are two work arounds. I don't like either one but they have different trade-offs:
1)
If your Rule exposes clean up methods, you can manually invoke the clean up inside a @Before
method.
@ClassRule
public static MyService service = ... ;
@Before
public void cleanupBetweenTests(){
service.cleanUp();
}
The downside to this is you need to remember (and to tell others on your team) to always add that @Before method or create an abstract class that your tests inherit from to do the clean up for you.
2) Have 2 fields, one static, one non-static that point to the same object, each field annotated by either a @ClassRule
or @Rule
respectively. This is needed if the cleanup isn't exposed. The downside of course is you also have to remember to have both a @ClassRule and a @Rule point to the same thing which looks strange.
@ClassRule
public static MyService service = ... ;
@Rule
public MyService tmp = service ;
Then in your implementation you have to differentiate between a test suite or a single test. This can be done by checking if the Description
has any children. Depending on which one, you create different Statement
adapters to handle cleanup or not:
@Override
protected void after() {
//class-level shut-down service
shutdownService();
}
@Override
protected void before() {
//class-level init service
initService();
}
@Override
public Statement apply(Statement base, Description description) {
if(description.getChildren().isEmpty()){
//test level perform cleanup
return new CleanUpStatement(this,base);
}
//suite level no-change
return super.apply(base, description);
}
Here is the custom Statement
class to clean up before each test:
private static final class CleanUpStatement extends Statement{
private final MyService service;
private final Statement statement;
CleanUpStatement(MyService service, Statement statement) {
this.service = service;
this.statement = statement;
}
@Override
public void evaluate() throws Throwable {
//clear messages first
myService.cleanUp();
//now evaluate wrapped statement
statement.evaluate();
}
}
After all of that, I would lean more towards option 1 since it's more intent revealing and is less code to maintain. I would also worry about others trying to modify the code in option 2 thinking there was a bug since the same field is pointed to twice. The extra effort, code comments etc are not worth it.
Eitherway you still have boiler plate to copy and paste everywhere or abstract classes using a template method.