Search code examples
javaunit-testingjunit4code-reuse

Reusing test implementations in JUnit 4?


I have an interface, e.g.:

public interface Thing {
  FrobResult frob(FrobInput); 
}

And several implementations of that interface (e.g. NormalThing, ImmutableThing, AsyncThing) that I am trying to test.

Many of my test methods are really about ensuring that the interface is implemented correctly, and thus are duplicated across each Thing implementation. In JUnit 3 a common solution to this would be to create a base class (extending TestCase) that is then subclassed by each implementation class. But is this the correct approach for JUnit 4?

Possible alternatives in (I believe) ascending order of preference:

  1. Cut'n'paste the duplicated test methods. Not DRY at all, but I guess less worrisome in tests than it would be in production code.

  2. Create an abstract class with @Test methods, and subclass it for each implementation test class. (Commonly seen with JUnit 3 tests -- is this still a good way to go in JUnit 4?)

  3. Put the common test methods into a helper class, and invoke it on each implementation. (Composition instead of inheritance.)

What's the best practice for doing #3? Maybe a @RunWith(Parameterized.class) test that is parameterized with each implementation? Or is there a better way to accomplish this?


Solution

  • Yes, it is the correct approach to create a base class that is then subclassed by each implementation class in JUnit4, too.

    I prefer the base test class for the interface to be abstract, i.e. your "alternative" 2, since I have made good experience in mimicing the inheritance hierarchy from the production code for the test code. So if you have interface I and implementations S1, S2 and S3, you make the abstract test class TestI and the test classes TestS1, TestS2 and TestS3.

    Test cases should be speaking, i.e. tell a story. By choosing -- as always -- method names carefully and use clean behavioral subtyping only, inheritance does not obfuscate this.