2

So right now I'm using JUnit 4 and in the @BeforeClass methods I setup everything needed to reset the user schema or to prepare sample data. Now, it's not that I don't like this approach but I found it quite frustrating for the following reason:

  • I'm using the Parameterized annotation to run the very same tests with different input data. Parameterized doesn't work on @BeforeClass because @BeforeClass works with a static method.

This means I have to replicate tests if I want to keep the @BeforeClass logic. I can't use @After and @Before because those will happen after every test and it would be an overhead.

I was thinking I could refactor this Unit Tests in the sense that I'll write an abstract class that handles the test and a subclass for every group parameters I want to try so that I can have the test code written only once.

I'm hoping you can suggest a cleaner option with the following starting point: the use of @Parameterized, the need to run the "database" method only once per parameter group.

EDIT:

this is an example of my class without the BeforeClass

RunWith(LabelledParameterized.class)
public class TestCreateCampaign extends AbstractTestSubscriberCampaign {

    public TestCreateCampaign(String label, String apiKey, String userKey,
            int customerId) {
        super(label, apiKey, userKey, customerId);
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @Parameters
    public static Collection<Object[]> generatedData() {
        return DataProvider.generatedCorrectSubscriberData();
    }

    @Test
    public void testCreateEmailCampaignBothTriggered() {

        // TEST

    }

    @Test
    public void testCreateTextCampaignTriggered() {

        // TEST

    }

    @Test
    public void testCreateTextCampaignTest() {

        // TEST

    }

    // Other Tests

}
dierre
  • 7,140
  • 12
  • 75
  • 120
  • So currently, are you using `Enclosed` with multiple parameterized inner classes? Each uses a static setup method in the top-level class? Once per parameter group? Please clarify. – John B Oct 19 '12 at 12:38
  • When you say Parameterized doesn't work on BeforeClass, what do you mean? Do you mean the method doesn't run, or it runs in the wrong order? Can you post some sample code? – Matthew Farwell Oct 19 '12 at 12:52
  • @MatthewFarwell I believe he means that BeforeClass runs only once, not once per set of parameterized values. Before runs once for each test. I believe he wants once per parameterized set of values. – John B Oct 19 '12 at 12:54

3 Answers3

0

What about calling your setup method from the constructor of your parameterized test class?

Edit:

OK, do I don't know of anything that does this automatically, but I think you could code up a Rule to do it. You could either implement a Rule from scratch of extend ExternalResource. Here is what I think it would do.

  1. The constructor would take an instance of the test class and an ExternalResource instance.
  2. In the constructor it would find the list of methods that contain the @Test annotation a get a count. It would set an iteration count to 0.
  3. In the before method it would increment the iteration count and if it is 1 after increment (or 0 before) it would invoke the before method on the passed ExternalResource.
  4. In the after method it would check to see if the iteration count was equal to the number of tests and if so call the after method on the passed ExternalResource.

You might need to use a different callback class / interface and ExternalResource since the before and after methods are protected. If you really wanted to be cool, you would define your own BeforeParameters and AfterParameter annotations in your rule and it would look for those methods in the passed instance.

If you develop this please post it or submit it to JUnit for inclusion.

Here is what I came up with, not as nice as I would like:

@RunWith(Parameterized.class)
public class TestExample {

private interface BeforeAfter {
    void before();

    void after();
}

public static class Resource extends ExternalResource {

    private final int count;
    private final BeforeAfter ba;
    private int iteration = 0;

    Resource(Object instance, BeforeAfter ba) {
        int localCount = 0;
        for (Method method : instance.getClass().getMethods()) {
            if (method.getAnnotation(Test.class) != null) {
                localCount++;
            }
        }
        this.count = localCount;
        this.ba = ba;
    }

    @Override
    protected void before() throws Throwable {
        if (iteration == 0) {
            ba.before();
        }

        iteration++;
    }

    @Override
    protected void after() {
        if (iteration == count) {
            ba.after();

            iteration = 0;
        }
    }
}

@Parameters
public static Iterable<Object[]> data() {
    return Arrays.asList(new Object[][] { { 3, 0 }, { 4, 1 } });
}

@Rule
public static Resource resource = new Resource(new TestExample(0, 0), new BeforeAfter() {

    @Override
    public void before() {
        System.out.println("setup");
    }

    @Override
    public void after() {
        System.out.println("cleanup");

    }
});

private int fInput;
private int fExpected;

public TestExample(int input, int expected) {

    // System.out.println("Constructor invoked" + fInput);
    fInput = input;
    fExpected = expected;
}

@Test
public void test1() {
    System.out.println("test1 fInput=" + fInput);
}

@Test
public void test2() {
    System.out.println("test2 fInput=" + fInput);
}
}

Resulted in:

setup
test1 fInput=3
test2 fInput=3
cleanup
setup
test1 fInput=4
test2 fInput=4
cleanup
John B
  • 32,493
  • 6
  • 77
  • 98
  • added what you were asking to clarify. – dierre Oct 19 '12 at 12:55
  • Yeah, but does calling from the constructor not work for some reason? – John B Oct 19 '12 at 12:55
  • So, I know this would be a cluge but... you could have a count value equal to the number of tests (set in the constructor). In the After, decrement this value and if it is 0 call the AfterClass. – John B Oct 19 '12 at 13:12
  • Yeah sorry, I was aiming for something that would work by default in JUNIT4 with parameterized so also with AfterClass. – dierre Oct 19 '12 at 13:14
  • I tried what you suggested but the constructor is called after every method tested. Not after every parameter tested. – dierre Oct 19 '12 at 15:46
  • WOW! Yeah it does call the constructor every time. Really ODD! – John B Oct 19 '12 at 15:51
0

This depends on how you want to set up your classes, but you can use a ClassRule for this. This does the same job as a TestRule, but it runs once for each class, rather than each test. This can be combined with Parameterized and TestRule, such as:

@RunWith(Parameterized.class)
public class TestCreateCampaign {
  @ClassRule
  public static ExternalResource beforeAfterClass = new ExternalResource() {

    @Override
    protected void before() throws Throwable {
      System.out.println("before each class");
    }

    @Override
    protected void after() {
      System.out.println("after each class");
    }
  };

  @Rule
  public ExternalResource beforeAfter = new ExternalResource() {
    @Override
    protected void before() throws Throwable {
      System.out.println("before each test");
    }

    @Override
    protected void after() {
      System.out.println("after each test");
    }
  };

  @Parameters(name = "{index}: fib({0})={1}")
  public static Iterable<Object[]> data() {
    return Arrays.asList(new Object[][] { { 3, 0 }, { 4, 1 } });
  }

  private int fInput;
  private int fExpected;

  public TestCreateCampaign(int input, int expected) {
    fInput = input;
    fExpected = expected;
  }

  @Test
  public void test1() {
    System.out.println("test1 fInput=" + fInput);
  }
}

This produces the following output:

before each class
before each test
test1 3
after each test
before each test
test1 4
after each test
after each class

This seems to be what you're looking for. To cut down on the amount of duplication, you can of course define beforeAfterClass and beforeAfter in a separate java class.

These are available in JUnit 4.9+.

Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
  • What the OP is looking for is to run the "before class" and "after class" methods twice, once for each set of parameterized values. – John B Oct 19 '12 at 13:30
  • exactly as John B says I need to run it on a parameter basis. – dierre Oct 19 '12 at 16:01
  • Then I don't understand the use case. The @Parameters needs to be static and in a separate class, so if you want to have one set of parameters, you need to have one test class for that. Is your problem that you want to parameterise the test class, so you have one test class which you can hand a set of parameters, and that test class being itself parameterized? – Matthew Farwell Oct 19 '12 at 16:28
0

See How to load DBUnit test data once per case with Spring Test for a way of initialising your test data only once per test run.

Community
  • 1
  • 1
Kkkev
  • 4,716
  • 5
  • 27
  • 43