JUnit 4's @Suite, passing data, @Rule and deprecating @Before and @After

1.6k views Asked by At

I'm working with some legacy test code which makes use of a TestSetup class to setup and teardown a server around a test suite of classes containing tests. I've converted the classes to use junit annotations and have attempted to use the @Suite and @Suite.Classes annotations to define the suite. But I've hit a wall.

The old version of the tests extend TestSetup to loop through all the instantiated test classes and inject various references to server objects into them. An example being a reference to the Spring framework web context.

My problem is that using the annotations I cannot see how to pass fixture data to the instantiated test classes before the tests are executed.

I've looked into the new @Rule and MethodRule techniques, but (frankly) they just appear to be a complex, but limited solution to a problem that's not really clear. Anyway, I could not see how they could solve my problem. An additional concern being that it would appear that the JUnit authors are intent on eventually removing the @Before/@After concept from JUnit.

So does anyone know how to pass data from a class annotated with @Suite to each test that the Suite class runs?

2

There are 2 answers

2
Peter Niederwieser On BEST ANSWER

My problem is that using the annotations I cannot see how to pass fixture data to the instantiated test classes before the tests are executed.

JUnit4-style test suites are known to be more limited than JUnit3-style test suites. I've heard complaints about this but am not aware of an official solution. I think there might be some third-party solutions that try to tackle this problem. Regarding your particular problem, I don't think you can get access to the test instances from a JUnit4-style suite, because JUnit4 instantiates a test right before the method gets run.

The simplest way out would be to continue using the JUnit3-style suites. They should run fine with JUnit4. One drawback of managing shared resources with a suite is that you can't run the test classes separately anymore. On the other hand, if the test classes need to be run together anyway (maybe in a particular order), it may be a valid solution.

Another approach is to tackle the shared resources problem inside-out: In your case, test classes would declare with a @Rule that they need the server to be present. The first time the rule gets executed, it starts the server, keeps relevant information in static field(s), and does whatever is necessary to prepare the test. For example it could inject some of the test's fields, or it could just allow the test to use the rule's API to get at the information it needs. The next time the rule gets executed, it skips the "start server" step. Yes, static state is evil, but it's sometimes the only way to get around JUnit's limitations. (I won't touch the subject of using other testing frameworks here.)

The big advantage of the inside-out approach is that it allows you to run test classes directly (without going through the suite) and independently from each other. The drawback is that it can be more difficult to implement, and that it makes it hard to tear down non-memory resources (in your case: shutting down the server). To accomplish the latter, you will have to use a JVM shutdown hook because JUnit doesn't provide an adequate callback. Although this will probably work (because build tools and IDEs typically use a separate JVM for each test run), it is nevertheless ugly.

A third approach is to let your Ant/Maven/Gradle/whatever build set up/tear down common resources before/after running the tests. Again, this is less flexible and convenient than the inside-out approach, and brings up the question of how to pass information from the build to the JVM that's running the tests (if necessary). The Maven Cargo plugin is a typical example for this approach.

Last but not least, you could try to be pragmatic and start/stop the server once per test class. This is very simple to implement, but may or may not be adequate in your situation (e.g. it might be too time-consuming).

I've looked into the new @Rule and MethodRule techniques, but (frankly) they just appear to be a complex, but limited solution to a problem that's not really clear

Rules are a way to write modular and reusable JUnit extensions. Much better than using base classes, where you will soon hit the single inheritance problem. This is even more relevant for libraries shipping JUnit extensions.

An additional concern being that it would appear that the JUnit authors are intent on eventually removing the @Before/@After concept from JUnit.

Where did you hear that? Aren't you confusing this with xUnit.net, which strongly discourages the use of a setup/teardown method? Anyway, performing some action before or after a test is one of the most important use cases for rules, so I don't think you should be concerned.

0
Yishai On

JUnit 4.9 is cooking a better solution for this in the form of a builder, but I think before then the cleanest (although a bit difficult to implement) way to address this is to make a custom Runner implementation (extend one of the existing ones). @Peter is quite right that Suite creation has weak points over JUnit 3.8, although it also has some strong points.