Thursday, December 19, 2013

JUnit Testing Tips - Constructor is Called Before Executing Test Methods

Most of Java programmers either use JUnit or TestNG for there unit testing need, along with some mock object generation libraries e.g. Mockito, but not everyone spend time and effort to learn subtle details of these testing libraries, at least not in proportion of any popular framework e.g. Spring or Hibernate. In this blog post, I am sharing one of such details, which has puzzled me couple of years ago. At that time, though I had been using JUnit for significant time, I wasn't aware that code written inside constructor of Test class is executed before each test method.  This behaviour of JUnit has caused, some of my test to failed and putting hours of investigation in my code, without realizing that this is happening because of JUnit is initializing object by calling constructor before executing test method annotated with @Test annotation. I had following code to test my vending machine implementation as coding exercise. If you look at closely, I have initialized vending machine in class body, which is executed as part of constructor. I was assuming one instance of vending machine is shared between all test methods, as I was not using @Before and @After, JUnit 4 annotation for setup() and tearDown(). In first test, one item from Inventory is consumed and in second test another item, but when you assert count of items based upon previous test, it will fail, because you are testing a different vending machine instance, which has different inventory.  So always, remember that JUnit calls constructor of test class before executing test method. You can verify it by putting a System.out.println message in constructor itself.



Code Written in Constructor is Executed before each Test Method

JUnit Tips for Java ProgrammersHere is code example of a JUnit test, which will demonstrate this point.  In our JUnit test class VendingMachineTest we have two test methods, buyDrinkWithExactAmount()  and  buyDrinkWithExactAmount(), and we are printing message from constructor. In the output section, you can see that message from constructor has appeared two times, one for each test case. This proves that constructor of JUnit test class is executed before each test method.


import java.util.List;
import org.junit.Test;
import static org.junit.Assert.;

/**
  *
  * @author Javin
  */
public class VendingMachineTest {
    private VendingMachine machine = new VendingMachine();
  
    public VendingMachineTest(){
        System.out.println("JUnit Framework calls Constructor of test class before executing test methods");
    }

    @Test
    public void buyDrinkWithExactAmount(){
        int price = machine.choose(Item.COKE);
        assertEquals(70, price);
        assertEquals(Item.COKE, machine.currentItem);
      
        machine.insert(Coin.QUARTER);
        machine.insert(Coin.QUARTER);
        machine.insert(Coin.DIME);
        machine.insert(Coin.DIME);
        assertEquals(70, machine.balance);
        assertEquals(7, (int) machine.coinInvertory.getCount(Coin.DIME));
        assertEquals(7, (int) machine.coinInvertory.getCount(Coin.QUARTER));
      
        Item i = machine.dispense();
        assertEquals(Item.COKE, i);
        assertEquals(4, (int) machine.itemInvertory.getCount(i));
      
        List change = machine.getChange();
        assertTrue(change.isEmpty());
      
      
    }
  
    @Test
    public void buyDrinkWithMoreAmount(){
        int price = machine.choose(Item.SPRITE);
        assertEquals(90, price);
        assertEquals(Item.SPRITE, machine.currentItem);
      
        machine.insert(Coin.QUARTER);
        machine.insert(Coin.QUARTER);
        machine.insert(Coin.QUARTER);
        machine.insert(Coin.QUARTER);
      
        assertEquals(100, machine.balance);
        assertEquals(9, (int) machine.coinInvertory.getCount(Coin.QUARTER));
      
        Item i = machine.dispense();
        assertEquals(Item.SPRITE, i);
        assertEquals(4, machine.itemInvertory.getCount(i));

        //this was failing, because by default VM initialize inventory with 5 items
        assertEquals(4, machine.itemInvertory.getCount(Item.COKE));

      
        List change = machine.getChange();
        assertEquals(1, change.size());
        assertEquals(Coin.DIME, change.get(0));
        assertEquals(4, machine.coinInvertory.getCount(Coin.DIME));
             
    }

Output:
Testsuite: test.VendingMachineTest
JUnit Framework calls Constructor of test class before executing test methods
JUnit Framework calls Constructor of test class before executing test methods
Tests run: 2, Failures: 1, Errors: 0, Time elapsed: 1.421 sec

Result
------------- ---------------- ---------------
Testcase: buyDrinkWithMoreAmount(test.VendingMachineTest):      FAILED
expected:<4> but was:<5>
junit.framework.AssertionFailedError: expected:<4> but was:<5>
        at test.VendingMachineTest.buyDrinkWithMoreAmount(VendingMachineTest.java:63)

That's all guys. I feel this is a worth knowing detail if you are using JUnit for writing unit test in your Java project. Sometime, we have bug in unit test itself, but we suspect our code. So it must for all Java developers to know nitty gritty of JUnit testing framework as well

3 comments :

Anonymous said...

Well I am confused about how did your test fail? Each testcase should be an isolated entity and if that were the case, the test case would have never failed as each testcase would be given a new vendingMachine to perform the test.

eyalgo.com said...

As mentioned above, part of unit test paradigm is that each test should be isolated from the other.
If one test affects another, the tests design is wrong.

Having said that, if you do want a shared service for all your tests, which will be initialized only once (DB connection?), you can use the @BeforeClass annotation on a static method.

BTW,
I have never used constructor in Test class, unless I use JUnit parameters.

rop said...

I just felt you are not spelling out the complete story...

Yes, JUnit "calls the constructor" before each test, but that is only because it is actually creating a separate instance of the whole test-class for each method to be called.

So if you have, for example, a test-class with 5 test-methods, it will create 5 separate instances of the test-class and invoke only one method on each test-class.

You can easily verify this, by printing the value of: this.hashCode()
in the constructor, and in each test-method to see exactly what is going on.
It will show you that each test-method is invoked on a different instance of the test-class.

I guess it is implemented this way, to guarantee that the test-methods are as isolated and independent as possible from each other.

Also, per simple experiments, in JUnit 3.8, ALL the test-class instances are created BEFORE all the test-methods are invoked,
while in JUnit 4.11, it will create one instance and call the test-method, then create the next instance and call the test-method, etc,
(which is better, of course, since it requires less total memory-resources).

Post a Comment