Search code examples
javaunit-testingtestingjunitjunit5

How does Junit test work in the background?


I just started with Junit Testing very recently, so please excuse me if my terminology is not correct. As far as I know, the methods become a test method if we annotate it with a @test and we can run it with an IDE(I am using Intellij) by clicking on the play button next to it. In the background, it probably instantiates an instance of the Test class and run the selected method. My question is, in the example below, if test1 and test2 both use and edit the same hello string of the same instance of the Test class, since test2 runs after test1, it should print the edited version of the hello String? i.e: test2 should print "1". But in this case it prints an empty String. Why is this ?

class Test{
   private  String hello = "";

   @Test
   void test1() {
      hello = hello + "1";
      System.out.println(hello);
   }
   @Test
   void test2() {
      System.out.println(hello)
   }
}

Solution

  • This happens due to the lifecycle of the test. You've guessed correctly, indeed JUnit instantiates the Class of the test (class Test in this case).

    In other words somewhere in the code of JUnit there is a line: Test test = new Test() (in the real code it uses reflection, but it doesn't matter for the sake of the question).

    Then it runs the test methods, so far so good - you were right:

    Test test = new Test();
    
    test.test1();
    
    test.test2();
    // handle failures, exceptions, etc.
    

    However, JUnit is designed in a way that it instantiates the class of test before running every single test method. This is a design decision of the JUnit framework.

    So, the "more accurate" representation of what happens in JUnit looks like this:

    Test test = new Test();
    
    test.test1();
    
    ....
    
    test = new Test();
    
    test.test2();
    

    This is a default behavior of JUnit, all the data fields get instantiated over and over again for each test and it guarantees that test1 prints 1 and test2 prints an empty string.

    This happens regardless the order of actual tests execution.

    Now, this behavior can be altered by using @TestInstance(LifeCycle.PER_CLASS) annotation on the test class, but its a pretty advanced stuff. It will instruct the JUnit engine to create only one single object of class Test and run all the test methods in it. So if one test method will change the data field defined in the class Test the other test will actually see this change.

    If you're interested to learn more about @TestInstance - consider reading this article for example