0

I have what amounts to a lightweight test framework written as a JUnit Abstract test. What I would like to do is have the implementing subclasses each define their custom test class setup. My plan was to have the abstract superclass define an @BeforeClass method that calls into an abstract setup method that each subclass would be forced to define, but this fails as the @BeforeClass methods must be static and static methods cannot be made abstract nor can they call instance methods.

I could just assume that subclasses will do the setup by including what's required in the documentation or by throwing an IllegalStateException, but I'd really like to be able to enforce this at an interface level for a number of reasons. Can anyone think of a work around for this?

By the way, I had the same issue with making these tests parameterized (subclasses define the parameters, but @Parameters annotated methods must be static). I got around this by running with the 3rd party JUnitParams runner which allows method level parameters. Check it out here: https://github.com/Pragmatists/JUnitParams

Adam Michalik
  • 9,678
  • 13
  • 71
  • 102
Jeremy
  • 180
  • 2
  • 8
  • Do you require the this call be only called once or can it be run multiple times? – dkatzel Nov 06 '13 at 20:30
  • 1
    Just once with each test class. I could run it for each test method using @Before, but that would be wasteful. – Jeremy Nov 06 '13 at 20:39

4 Answers4

1

For your main question, why not make your parent class abstract and use the @Before annotation instead of @BeforeClass ? For example:

public abstract class TestParent {
   @Before
   public void setup() {
     doSetup();
   }

   protected abstract void doSetup();

   // other stuff...
}

public class RealTest extends TestParent {
    protected void doSetup() {
      // custom setup
    }

    // custom tests...
}

This will force the subclasses to redefine the doSetup() method without using static methods.

Fouad HAMDI
  • 456
  • 2
  • 4
  • 2
    `@BeforeClass` will only be executed once where as `@Before` will get executed before every test – dkatzel Nov 06 '13 at 20:28
  • Exactly. I only want the setup to happen once. @Before will happen before each test method. – Jeremy Nov 06 '13 at 20:35
  • 1
    I understand better your question. For such a situation, you may use reflection in your parent class @BeforeClass method to check the existence of your subclass static "conventional" method: if it exists, call it, if not throw some exception. – Fouad HAMDI Nov 06 '13 at 20:44
  • Thanks, that seems tantamount to just throwing an IllegalStateException if the setup was never done though. What I want is for test writers to be able to click te "add uniplemented methods" button on their IDE and have a template ready to go. I don't want them to have to deal with RuntimeExceptions. Perhaps I'm being a bit pedantic about this but it seems like this should be doable. – Jeremy Nov 06 '13 at 20:48
  • I think that the "static" feature of @BeforeClass will prevent you to do that. If your concern is only to generate code for your developers, there is also the possibility to create some plugin or some template for the IDE if you all share the same. They will just use your custom wizard or shortcut to generate the starting skeleton of the tests. – Fouad HAMDI Nov 06 '13 at 20:59
1

One option is to have subclasses implement a, say, static doSetupOnce() method, and find and invoke that method reflectively from the base class @BeforeClass method. Since this needs to be a static method, its existence can only be enforced at runtime.

Another approach would be to have an abstract doSetupOnce instance method in the base class which gets invoked the first time the parent's @Before method gets invoked. This enforces the issue at compile time, but then implementors will have to be careful not to access instance fields from this method (since this is probably not what they want).

In general (and without knowing the details of your situation), I'm not very fond of either of these approaches, and would rather leave it up to implementors to declare a @BeforeClass method if needed. Locking them up in a rigid base class scheme can cause more problems than it solves. Also consider the use of JUnit rules, which often are a better choice than base classes (e.g. because they are composable). Of course you can also combine the two approaches, relying mainly on JUnit rules and additionally offering some base classes with predefined rules for convenience.

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • Thanks, I really like your middle solution as it fits my purposes. I agree that it makes the test a bit rigid and is a bit of a smell, but these tests are such that they require ALOT of setup and then execute a small amount of actual test code. I'll take another look at the JUnit rules as I'm only passingly familiar with them. – Jeremy Nov 06 '13 at 21:03
1

This may be out of scope or overkill, but I think it's worth mentioning since they're not that different. So I'm taking a leap of faith and suggest that you could try TestNG because methods annotated with @BeforeClass do not have to be static, nor do those annotated with @Parameters.

You can read about the differences between the 2 frameworks here and it looks like they also have support for migrating JUnit tests to TestNG

Morfic
  • 15,178
  • 3
  • 51
  • 61
1

I don't think it is possible to do this in a clean OO way. Not only is @BeforeClass a static method, but JUnit will call the parent's @BeforeClass before the child's @BeforeClass.

Anyway you try to do this must necessarily expose the parent class's internal static state so a child class can set the parent's fields, breaking encapsulation.

I think the best way is to to use @Before, but also have a static flag that sets if the method has been called before, that way at least you can short circuit and only do the initialization for the first call...

dkatzel
  • 31,188
  • 3
  • 63
  • 67