tags:

views:

1001

answers:

6

I marked a method with jUnit's @BeforeClass annotation, and got this exception saying it must be static. What's the rationale? This forces all my init to be on static fields, for no good reason as far as I see.

In .Net (NUnit), this is not the case.

Edit - the fact that a method annotated with @BeforeClass runs only once has nothing to do with it being a static method - one can have a non-static method run only once (as in NUnit).

+4  A: 

JUnit documentation seems scarce, but I'll guess: perhaps JUnit creates a new instance of your test class before running each test case, so the only way for your "fixture" state to persist across runs is to have it be static, which can be enforced by making sure your fixtureSetup (@BeforeClass method) is static.

Blair Conrad
Not only perhaps, but JUnit definitely creates a new instance of a test case. So this is the only reason.
furtelwart
This is the only reason they have, but in fact the Junit runner *could* do the job of executing a BeforeTests and AfterTests methods the way testng does.
HDave
Does TestNG create one instance of the test class and share it with all tests in the class? That makes it more vulnerable to side-effects between tests.
Esko Luontola
+3  A: 

there are two types of annotations:

  • @BeforeClass (@AfterClass) called once per test class
  • @Before (and @After) called before each test

so @BeforeClass must be declared static because it is called once. You should also consider that being static is the only way to ensure proper "state" propagation between tests (JUnit model imposes one test instance per @Test) and, since in Java only static methods can access static data... @BeforeClass and @AfterClass can be applied only to static methods.

This example test should clarify @BeforeClass vs @Before usage:

public class OrderTest {

    @BeforeClass
    public static void beforeClass() {
        System.out.println("before class");
    }

    @AfterClass
    public static void afterClass() {
        System.out.println("after class");
    }

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

    @After
    public void after() {
        System.out.println("after");
    }    

    @Test
    public void test1() {
        System.out.println("test 1");
    }

    @Test
    public void test2() {
        System.out.println("test 2");
    }
}

output:

------------- Standard Output ---------------
before class
before
test 1
after
before
test 2
after
after class
------------- ---------------- ---------------
dfa
I find your answer irrelevant.I know the semantics of BeforeClass and Before. This doesn't explain why it has to be static...
ripper234
"This forces all my init to be on static members, for no good reason as far as I see." My answer should show you that your init can be also *non-static* using @Before, instead of @BeforeClass
dfa
I'd like to do some of the init one time only, at the start of the class, but on non-static variables.
ripper234
you cannot with JUnit, sorry. You must use a static variable, no way.
dfa
If the initialization is expensive, you could just keep a state variable to record whether you've done the init, and (check it and optionally) perform the init in a @Before method...
Blair Conrad
static is just a *simpler* solution
dfa
+3  A: 

The short answer is this: there is no good reason for it to be static.

In fact, making it static causes all sorts of problems if you use Junit to execute DBUnit based DAO integration tests. The static requirement interferes with dependency injection, application context access, resource handling, logging, and anything that depends on "getClass".

HDave
+1  A: 

I've created a bug tracker entry for this:

http://github.com/KentBeck/junit/issues/issue/122/

Seems like nobody had reported this design flaw before ...

EDIT

found a nice workaround called class initializer:

public class BlaTest(){

  {
    //Called only once after constructor for a new instance has been run
  }

} 

it works just like the static{} initializer which runs when the class gets initialized.

subes
Good work. Upvoted (your answer + issue)
ripper234
That will be executed once per test, because JUnit creates once instance of the class per test. It's practically the same as @Before, not @BeforeClass. Just put a println statement there to see it yourself.
Esko Luontola
Also the instance initializers are not executed "_after_ constructor for a new instance has been run". The code in the initializers is compiled into the beginning of the constructors, right after calling the constructor of the superclass. In http://www.developer.com/java/other/article.php/3065621/The-Essence-of-OOP-using-Java-Instance-Initializers.htm it's said that "The code in an instance initializer block is executed after the constructor for the superclass is executed, and before the constructor for the class to which the initializer belongs is executed."
Esko Luontola
A: 

It seems that JUnit creates a new instance of the test class for each test method. Try this code out

public class TestJunit {

int count = 0;

@Test
public void testInc1(){
    System.out.println(count++);
}

@Test
public void testInc2(){
    System.out.println(count++);
}

@Test
public void testInc3(){
    System.out.println(count++);
}

}

The output is 0 0 0

This means that if the @BeforeClass method is not static then it will have to be executed before each test method and there would be no way to differentiate between the semantics of @Before and @BeforeClass

Gunjan
+1  A: 

JUnit always creates one instance of the test class for each @Test method. This is a fundamental design decision to make it easier to write tests without side-effects. Good tests do not have any order-of-run dependencies (see F.I.R.S.T) and creating fresh instances of the test class and its instance variables for each test is crucial in achieving this. Some testing frameworks reuse the same test class instance for all tests, which leads to more possibilities of accidentally creating side-effects between tests.

And because each test method has its own instance, it makes no sense for the @BeforeClass/@AfterClass methods to be instance methods. Otherwise, on which of the test class instances should the methods be called? If it would be possible for the @BeforeClass/@AfterClass methods to reference instance variables, then only one of the @Test methods would have access to those same instance variables - the rest would have the instance variables at their default values - and the @Test method would be randomly selected, because the order of methods in the .class file is unspecified/compiler-dependent (IIRC, Java's reflection API returns the methods in the same order as they are declared in the .class file, although also that behaviour is unspecified - I have written a library for actually sorting them by their line numbers).

So enforcing those methods to be static is the only reasonable solution.

Here is an example:

public class ExampleTest {

    @BeforeClass
    public static void beforeClass() {
        System.out.println("beforeClass");
    }

    @AfterClass
    public static void afterClass() {
        System.out.println("afterClass");
    }

    @Before
    public void before() {
        System.out.println(this + "\tbefore");
    }

    @After
    public void after() {
        System.out.println(this + "\tafter");
    }

    @Test
    public void test1() {
        System.out.println(this + "\ttest1");
    }

    @Test
    public void test2() {
        System.out.println(this + "\ttest2");
    }

    @Test
    public void test3() {
        System.out.println(this + "\ttest3");
    }
}

Which prints:

beforeClass
ExampleTest@3358fd70    before
ExampleTest@3358fd70    test1
ExampleTest@3358fd70    after
ExampleTest@6293068a    before
ExampleTest@6293068a    test2
ExampleTest@6293068a    after
ExampleTest@22928095    before
ExampleTest@22928095    test3
ExampleTest@22928095    after
afterClass

As you can see, each of the tests is executed with its own instance. What JUnit does is basically the same as this:

ExampleTest.beforeClass();

ExampleTest t1 = new ExampleTest();
t1.before();
t1.test1();
t1.after();

ExampleTest t2 = new ExampleTest();
t2.before();
t2.test2();
t2.after();

ExampleTest t3 = new ExampleTest();
t3.before();
t3.test3();
t3.after();

ExampleTest.afterClass();
Esko Luontola