With jUnit 4, can I parameterize @BeforeClass?
Asked Answered
T

5

17

I am using jUnit to manage integration tests for an application that accesses a database. Because setting up the test data is a time-consuming operation, I have been doing that in the @BeforeClass method, which is executed only once per test class (as opposed to the @Before method, which is run once per test method).

Now I want to try a few different permutations for the configuration of the data layer, running all of my tests on each different configuration. This seems like a natural use of the Parameterized test runner. Problem is, Parameterized supplies parameters to the class constructor, and the @BeforeClass method is abstract and is called before the class constructor.

A few questions,

Does Parameterized call the @BeforeClass method for each permutation of parameters, or does it only call once?

If the @BeforeClass method is called repeatedly, is there some way to access the parameter values from inside of it?

If none of these, what do people suggest as the best alternative approach to this problem?

Triumphant answered 22/6, 2012 at 20:47 Comment(3)
See if code.google.com/p/junitparams can helpChlorothiazide
So there is still no way of doing this ?Thesis
The effect can be accomplished by a custom test runner. Typically you would subclass BlockJUnit4ClassRunner.Triumphant
S
3

I think you are going to need a custom test runner. I'm having the same issue you are having (needing to run the same tests using multiple, expensive configurations). You'd need a way to parameterize the set up, perhaps using @Parameter annotations similar to those used by the Parameterized runner but on static member fields instead of instance fields. The custom runner would have to find all static member fields with the @Parameter annotation and then run the test class (probably using the basic BlockJunit4ClassRunner) once per static @Parameter field. The @Parameter field should probably be a @ClassRule.

Andy on Software has done a good job of developing custom test runners, and he explains so pretty clearly in these blog posts here and here.

Spectrum answered 25/11, 2013 at 21:15 Comment(1)
+1 for @ClassRule which works perfectly without interfering with the Parameterized functionality or requiring a custom runnerDambrosio
I
1

@BeforeClass is only called once in your example. Which makes sense given the name - before class!

If your tests require different data, there are two choices I can think of:

  1. Set up that data in @Before so it is test specific
  2. Group the tests that you want to run with the same data into separate test classes and use @BeforeClass for each one.
Ionopause answered 27/6, 2012 at 2:32 Comment(3)
Thank you for clarifying my understanding of @BeforeClass.Unfortunately, the two approaches that you outline don't solve my larger problem. The problem with approach 1 is that I have high-cost one-off initialization that changes from one scenario to the next, but that I don't want to have to repeat for each test. The problem with approach 2 is that I would have to write a different test class for each scenario, even though I'm running the same tests.Triumphant
Dan, For option #2, you could subclass. Have the "same tests" in the superclass and then each subclass reflect the scenario. In other words, the subclasses just contain @BeforeClassIonopause
That approach would technically work, but I don't find it appealing, since I already have dozens of test classes, and expect eventually to have hundredsTriumphant
T
0

You can call this initialization logic in the constructor of your test class. Keep track of the last parameter used in a static variable. When it changes, set up the class for the new parameter.

I can't think of an equivalent for AfterClass.

Thoughtless answered 12/2, 2013 at 22:42 Comment(2)
Using the constructor instead of @Before doesn't help, as the standard JUnit 4 test runner creates a new instance of the test class for each test method. So I would still have the problem that my costly setup code is called once for every method and permutation of peramters, rather than just once for each permutation of parameters.Triumphant
Good catch. I've updated my answer (and the tests I'm working on with the same problem).Thoughtless
A
0

This is an old question, but I just had to solve a probably similar problem. I went with the solution below for now, which essentially is an implementation of TREE's (updated) answer with using a generic abstract base class in order to avoid duplication whenever you need this mechanism.

Concrete tests would provide a @Parameters method that return an iterable of single-element arrays containing a Supplier< T > each. Those suppliers are then executed exactly once per actual input needed by the concrete test methods.

@RunWith(Parameterized.class)
public class AbstractBufferedInputTest<T> {

private static Object INPUT_BUFFER;

private static Object PROVIDER_OF_BUFFERED_INPUT;

private T currentInput;

@SuppressWarnings("unchecked")
public AbstractBufferedInputTest(Supplier<T> inputSuppler) {
    if (PROVIDER_OF_BUFFERED_INPUT != inputSuppler) {
        INPUT_BUFFER = inputSuppler.get();
        PROVIDER_OF_BUFFERED_INPUT = inputSuppler;
    }
    currentInput = (T) INPUT_BUFFER;
}

/**
 * 
 * @return the input to be used by test methods
 */
public T getCurrentInput() {
    return currentInput;
}

}
Askance answered 23/9, 2015 at 7:31 Comment(0)
T
-1

You could do your initialization in a @Before method, writing to an instance variable but testing for null.

@RunWith(value = Parameterized.class)
public class BigThingTests {
  private BigThing bigThing;

  @Before
  public void createBitThing() {
    if (bigThing == null) {
      bigThing = new BigThing();
    }
  }

...
}

A new instance of BigThingTests is created for each set of parameters, and bigThing is set to null with each new instance. The Parameterized runner is single-threaded, so you don't have to worry about multiple initializations.

Transcription answered 1/2, 2013 at 16:50 Comment(1)
this.bigThing will always be null, as you get a new instance of BigThingTests for each @Test.Medawar

© 2022 - 2024 — McMap. All rights reserved.