Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 449
Daniel Pryden Avatar asked Nov 01 '12 21:11

Daniel Pryden


People also ask

How do you repeat a JUnit test?

JUnit Jupiter @RepeatedTest annotation is used to repeat the test case for specified no. of times. Each invocation of the test case behaves like a regular @Test method, so it has support for the same lifecycle callbacks and extensions in JUnit 5.

Is JUnit 4 still supported?

The platform changes in a highly incompatible way and the Vintage engine will no longer be supported. This is not to be expected in the near future.

What is the use of @rule in JUnit?

To use the JUnit rules, we need to add the @Rule annotation in the test. @Rule: It annotates the fields. It refer to the rules or methods that returns a rule. The annotated fields must be public, non-static, and subtypes of the TestRule or MethodRule.

Which package should you import while using JUnit?

Explanation: The default JUnit package is “org,junit”.


2 Answers

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.

like image 149
DaveFar Avatar answered Oct 18 '22 09:10

DaveFar


I use #2 approach for JUnit and TestNG test cases. This is most convenient and easy to maintain. Its also straight forward to pick up (since its native to OOD to have base class that has common methods). To me unit test classes are no different than regular project classes...so I do apply similar design considerations.

like image 34
user1697575 Avatar answered Oct 18 '22 09:10

user1697575