27

When i run the below code, both test cases come true:

import static junit.framework.Assert.assertEquals;

import org.junit.Test;

public class MyTest{
    private int count;

    @Before
    public void before(){
        count=1;
    }

    @Test
    public void test1(){
        count++;
        assertEquals(2, count); 
    }

    @Test
    public void test2(){
        count++;
        assertEquals(2, count); 
    }
}

EXPECTED BEHAVIOUR

  1. test1 - success
  2. test2 - fail(as expected that count will become 3)

ACTUAL BEHAVIOUR

  1. test1 - success
  2. test2 - success

Why junit is reinitializing class/variable with each test method invocation. It is a bug in junit or is provided intentionally.

Prateek
  • 12,014
  • 12
  • 60
  • 81

6 Answers6

25

It is because of test isolation.

No test should depend on another.

René Link
  • 48,224
  • 13
  • 108
  • 140
  • In this example, they don't depend on one another – Sean Owen Oct 15 '13 at 12:40
  • 2
    @Sean Owen yes but if junit would reuse the MyTest instance for every test method it could be possible that the tests affect each other. That is why junit creates a new instance of MyTest for each test method. If the tests could affect each other it would be hard to find the failure's cause, wouldn't it? – René Link Oct 15 '13 at 12:43
  • I agree wholeheartedly with making tests independent. I thought the question was more about how to correctly achieve it but I am not so sure now so this is actually maybe the salient point. – Sean Owen Oct 15 '13 at 12:52
21

New Instance of MyTest for each test method

For each test method a new instance of MyTest will be created this is the behavior of Junit.

So in your case for both methods the variable count will have value 1, and thus the value of count++ will be 2 for both the test methods and hence the test cases pass.

public class MyTest{
   public MyTest(){
      // called n times
      System.out.println("Constructor called for MyTest");
   }

   @Before //called n times
   public void setUp(){
      System.out.println("Before called for MyTest");
   }
    
   //n test methods
}

If you execute the code above with 2 test methods:

Output will be:

Constructor called for MyTest
Before called for MyTest
//test execution
Constructor called for MyTest
Before called for MyTest

Edit:

Isolation from the F.I.R.S.T principle of testing

Test frameworks help you in doing the right thing, a very important property of unit tests is isolation.

By creating a new instance every test method, the dirty SUT is thrown away. So that we have a fresh state for every test.

Read about F.I.R.S.T principle of testing.

Narendra Pathai
  • 41,187
  • 18
  • 82
  • 120
10

Look at the documentation of org.junit.runner.Runner:

The default runner implementation guarantees that the instances of the test case class will be constructed immediately before running the test and that the runner will retain no reference to the test case instances, generally making them available for garbage collection.

Unit tests should be independant otherwise it becomes unmaintable. Note that the order of executed methods is not guaranteed (unless you use the annotation @FixMethodOrder).

LaurentG
  • 11,128
  • 9
  • 51
  • 66
5

Answer for JUnit 5

In JUnit5, this behavior is controlled using the @TestInstance annotation. The annotation can take as value one of two lifecycle qualifiers:

  • @TestInstance(Lifecycle.PER_CLASS): The test class will be initialized once for all the methods in the class.
  • @TestInstance(Lifecycle.PER_METHOD): The test class will be reinitialized before each test method (the behavior described in other answers).

If a test class is not annotated with @TestInstance, the default behavior is PER_METHOD.

For more details, see Test Instance Lifecycle in the JUnit5 User Guide.

SDJ
  • 4,083
  • 1
  • 17
  • 35
3

If you want to use test class's member variable for all tests , without it being reinitialised to null, then make it static.

Deepthi
  • 495
  • 4
  • 12
0

Don't initialize test class state in a constructor unless it is immutable.

JUnit does not instantiate your test class for every @Test. In fact it only runs methods marked @Before before each one, and will run @BeforeClass methods once before all of the tests in the class.

However you are not guaranteed that the test runner will actually use just one instance of your test class to run the tests. It is free to use many -- consider running a bunch of tests in parallel, even on different machines.

While there are JUnit runner settings to control this in general, it's much better to simply follow the JUnit design and initialize test state in a method marked @Before only.

Sean Owen
  • 66,182
  • 23
  • 141
  • 173
  • Your words are contradictory. JUnit 'does' instantiate test class for every @Test for most cases, doesn't it? – Wood May 03 '22 at 13:42
  • I don't think so, but I confess all these years later I don't know about JUnit 5. But what would be the point of the @Before annotations et al if so? I don't see the contradiction here - the point then was, it does not necessarily reinstantiate the class. It might in some situations, but there is no need to depend on it either way – Sean Owen May 16 '22 at 03:01