I have a number of test cases in JUnit. All of them need the same code to be executed in their @BeforeClass
static method. It's a code duplication and I'm trying to get rid of it. A dirty way of doing this is by inheritance. Are there any other mechanisms in JUnit, that may help?
PS. I wrote this blog post about this very subject: http://www.yegor256.com/2015/05/25/unit-test-scaffolding.html
The JUnit way to compose reusable code (instead of inheriting from it) are Rules.
See https://github.com/junit-team/junit/wiki/Rules
Here is a dumb sample, but you'll get the point.
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;
import org.junit.runner.Description;
public class MyTestRule implements TestRule {
@Override
public Statement apply(final Statement statement, Description description) {
return new Statement() {
public void evaluate() throws Throwable {
// Here is BEFORE_CODE
try {
statement.evaluate();
} finally {
// Here is AFTER_CODE
}
}
};
}
}
You can then use your TestRule like this:
import org.junit.Rule;
public class MyTest {
@Rule
public MyTestRule myRule = new MyTestRule();
}
BEFORE_CODE and AFTER_CODE will then be executed around each of your test methods.
If you need to run your code only once per class, use your TestRule as a @ClassRule:
import org.junit.ClassRule;
public class MyTest {
@ClassRule
public static MyTestRule myRule = new MyTestRule();
}
Now, BEFORE_CODE
and AFTER_CODE
will be executed around each of your test class.
@Rule field is not static, @ClassRule field is.
A @ClassRule can be declared in a Suite too.
Note that you can declare several rules in a single test class, that's how you compose test lifecycles at test-suites, test-classes and test-methods levels.
A Rule is an object that you instanciate in your test classes (statically or not). You can add contructor parameters if needed.
HTH
If the method is some kind of utility, then separate it out to a different class with a static method and call that method in your @BeforeClass.
I emphasize on the fact that don't use inheritance just because it solves your problem, use it when doing so creates sense in your class hierarchy.
You may create test runner
public class MyTestRunner extends BlockJUnit4ClassRunner {
@Override
protected Object createTest() throws Exception {
Object test = super.createTest();
doStuff();
}
public void doStuff(){
//common code
}
}
@RunWith(MyTestRunner.class)
public class MyTest1{
@Test
public void test1(){
//test method
}
}
Static methods aren't inherited, so inheritance isn't an option by default. If you mean you're moving the method to a common parent class, then that seems a poor choice since you only get one parent in Java. A test support class of some sort would seem more appropriate. It's also possible that you're seeing a need for a parameterized test.
There is absolutely nothing wrong with inheritance in this case, it's actually the only way to avoid repeating this code in each subclass. The fact that @BeforeClass methods have to be declared static in JUnit is unfortunate, but that shouldn't stop you. Extend the class and you have the initialization code automatically run for you without having to do anything.
If each and every class needs to have a @BeforeClass
annotated method that is exactly the same as every other, then inheritance does not feel that wrong to me. If each of these initializing methods simply share some code, you could make a TestUtil
class with some shared behavior and make calls to this shared behavior from each of the @BeforeClass
methods.
I think if the classes has "is-a" relation, inheritance is reasonable.
If the base class is MyBeforeClass
which defines @BeforeClass
method, and MyTestClass1
"is-a" MyBeforeClass
, MyTestClass1 extends MyBeforeClass
is OK.
Depending on the nature of the setup code, you can potentially put all your tests in a test suite and have the setup code run there. The downside to this is that you cannot run tests individually (since the test depends on the setup code).
It is test code, and it is not meant for heavy re-use. Do not over-engineer. Do not apply all the design patterns that you know. For test code, the rules are different.