11

I implemented a JUnit 4 TestRule (extending an ExternalResource), and injected it as a @ClassRule in my test class: I want to initialize a resource once for all in every test of this class, and tear it down eventually.

My issue is that my @Before and @After rule-methods are not called at all before/after my @Test method: any idea why this is happening?

Minimal compilable example:

package com.acme.test;

import static org.junit.Assert.assertNull;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.ExternalResource;

class Coffee {
    public void throwAway() {}
}

class CoffeeMachine extends ExternalResource {
    Coffee whatElse;    
    @Override protected void before() throws Throwable {
        whatElse = new Coffee();
    }

    @Override protected void after() {
        whatElse.throwAway();
    }

    public Coffee gimmieCoffee() { return whatElse; }
}

public class CoffeeTester {
    @ClassRule public static CoffeeMachine CM = new CoffeeMachine();

    @Test public void drinkACoffee() {
        Coffee c = CM.gimmieCoffee();
        assertNull(c);  // ---> Coffee is null!!                       (fuuuuuuuuuu...)
    }
}

Is there something I am misunderstanding here? Note that the same happens with a non-static @Rule.

I am using JUnit 4.11.

Thank you very much for any hint.

Campa
  • 3,502
  • 3
  • 30
  • 34
  • shouldn't the `@Before` be in the same class as the `@Test`? I'm also not sure if overriding `before()` will suffice if it is not also tagged – Dragondraikk May 12 '15 at 15:30
  • Not like that... but thank you Dragondraikk. – Campa May 12 '15 at 15:54
  • You clearly haven't tried the code you've provided, because you've got too many `m`s in the call to `gimmieCoffee` - and no imports. I've fixed up those things, and it's fine. Please provide an example which compiles and actually demonstrates the issue. – Jon Skeet May 12 '15 at 16:02
  • Jon, I just wanted to represent a minimal example, I did not run my coffee machine, but a typo does not really matter here, I was more trying to discover some more fundamental mistake I was doing here. – Campa May 13 '15 at 06:36
  • If I wanted to let you run the code, I would have posted a Coffee class too. Thanks for trying to help anyway Jon. I personally don't see this as a -1 question. – Campa May 13 '15 at 06:40
  • 2
    The example is now complete for copy-paste-compile ;) – Campa May 13 '15 at 11:03
  • 1
    @Campa if you want people to help with your question, the minimal code you post really *should* compile and be copy-pastable. I have run your code, as it stands above, and the `before()` and `after()` are being called. The `assertNull(c)` line for me gives: *java.lang.AssertionError: expected null, but was:*. I can't see why it isn't working for you. Running junit 4.11 and java 7 here. – vikingsteve May 15 '15 at 08:04
  • @vikingsteve: ..but my example is indeed directly compilable.. (thanks Jon) Anyway, good to know that it works for you there. The only difference here is that I am using Java 8 and that I tried within Eclipse IDE (Luna). I'll try to run this example manually, and as well with Java 7. – Campa May 15 '15 at 09:34
  • Coffee is _null_ here, with Java 7 too. – Campa May 15 '15 at 09:52
  • It _seems_ it is a problem with the version of JUnit shipped with Eclipe. I tried running the test via commad-line using v4.12, and all works as expected. It seems it is not really easy to change the version of JUnit used by Eclipse run configurations. http://stackoverflow.com/questions/10779156/how-to-select-or-change-junit-version-in-eclipse – Campa May 18 '15 at 09:32
  • If different classloaders are used, such strange phenomena can also happen. Can you add System.out.println(this.getClass().getClassLoader()); in before() and in drinkACoffee() and run the test again? – mmirwaldt May 21 '15 at 21:17
  • Hey @mmirwaldt: thanks for chiming in. Here is it: `sun.misc.Launcher$AppClassLoader@30dae81` – Campa May 22 '15 at 09:59
  • Is that the class loader for both outputs? If it is the same, my assumption is false. – mmirwaldt May 23 '15 at 11:31
  • There are no `@Before` and `@After` annotations in the code... – Johannes Jander Mar 05 '16 at 12:11

2 Answers2

4

I think this is a problem with your test runner. Maybe some plugin has installed a custom runner which is used when you run your tests from Ecilpse?

Check the run configuration for your test and make sure that the standard JUnit 4 test runner is used:

enter image description here

K Erlandsson
  • 12,359
  • 6
  • 46
  • 64
3

I see no issue here, but just a misunderstanding. First of all, let's read assert as it must be and change your code a bit (it is obvious your test says c must not be null which gives us: assertNotNull(c);

I've also added some output in order to show you what is going on. Please try to run it.

package com.acme.test;

import static org.junit.Assert.assertNotNull;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.ExternalResource;

class Coffee {
    public void throwAway() {}
}

class CoffeeMachine extends ExternalResource {
    Coffee whatElse;    
    @Override protected void before() throws Throwable {
        whatElse = new Coffee();
        System.out.println(" ### executing before: " + whatElse);
    }

    @Override protected void after() {
        whatElse.throwAway();
    }

    public Coffee gimmieCoffee() { return whatElse; }
}

public class CoffeeTester {
    @ClassRule public static CoffeeMachine CM = new CoffeeMachine();

    @Test public void drinkACoffee() {
        Coffee c = CM.gimmieCoffee();
        System.out.println(" ### executing test: " + c);
        assertNotNull(c); 
    }
}

For me it gives the following:

 ### executing before: com.acme.test.Coffee@28f67ac7
[VerboseTestNG] INVOKING: "com.acme.test.CoffeeTester" - com.acme.test.CoffeeTester.drinkACoffee()
 ### executing test: com.acme.test.Coffee@28f67ac7
[VerboseTestNG] PASSED: "com.acme.test.CoffeeTester" - com.acme.test.CoffeeTester.drinkACoffee() finished in 4 ms
[VerboseTestNG] 
[VerboseTestNG] ===============================================
[VerboseTestNG]     com.acme.test.CoffeeTester
[VerboseTestNG]     Tests run: 1, Failures: 0, Skips: 0
[VerboseTestNG] ===============================================

So c is not null as you expect it to be.

Renat Gilmanov
  • 17,223
  • 5
  • 35
  • 54
  • thank you for the help! I get `### executing test: null` however. :) – Campa May 15 '15 at 13:45
  • That is really fascinating :) Did you try another JUnit version? Can you provide check-sum of the jar you have? – Renat Gilmanov May 15 '15 at 21:13
  • I use the JUnit which is shipped with Eclipse: `org.junit_4.11.0.v201303080030/`. Checksum is fine .. I tried now with 4.12, my initial example works fine. – Campa May 18 '15 at 08:34
  • Thank you @Renat but.. 404! :D – Campa May 19 '15 at 08:31
  • @Campa, basically I just want us to have a proof your Eclipse environment is a bit "spoiled". BTW, do not forget to verify jars checksums in my archive or simply replace them. – Renat Gilmanov May 19 '15 at 08:52
  • I ran `ant` in the project folder, and the test runs successfully both with your JARs and with the ones shipped with Eclipse. Guess I should report this in the Eclipse forums. ? – Campa May 20 '15 at 13:18
  • 1
    I believe Eclipse is widely used, so it should not have such obvious issues. I can only suggest to experiment a bit (create a new project, play with executors a bit) in order to narrow down list of possible explanations. – Renat Gilmanov May 20 '15 at 16:11