Fork me on GitHub

Introduction

JUnit is the most popular test framework in the Java world. It is an instance of the xUnit architecture for unit testing frameworks.

During this workshop you will use JUnit 4. It allows you to configure tests, runners and other elements by annotations. Thanks to that code is more clear, readable and without redundancy.

For more information, please visit:

Exercise

In this part of the workshop you will add tests for RangedCalculator class. Please be noticed that this class is not well designed. We use some "uncommon" constructions to allow you learn more JUnit features. You should start by opening the RangedCalculatorTest.java file.

JUnit Basics

Let's say we have a Calculator class which should be tested:

public class Calculator {

    public Integer add(Integer arg1, Integer arg2) {
        // logic is here
    }
}

Now you have to add a new class with the same name and Test suffix:

public class CalculatorTest {

    // all tests will be here
}

Every method used as a test must be:

  • public
  • not static
  • returns void
  • take zero parameters
  • annotated with org.junit.Test
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void add() {
        // given
        final Calculator calculator = new Calculator();

        // when
        final Integer result = calculator.add(2, 5);

        // then
        // assertions here
    }
}

To verify test result you have to add at least one JUnit assertion. To do that you can use methods defined in org.junit.Assert class:

import org.junit.Assert;
import org.junit.Test;

public class CalculatorTest {

    @Test
    public void add() {
        // given
        final Calculator calculator = new Calculator();

        // when
        final Integer result = calculator.add(2, 5);

        // then
        Assert.assertNotNull(result);
        Assert.assertEquals(7, result.intValue()); 
    }
}

For better readability you can use static imports (recommended by us) and add messages to the assertions (not recommended by us):

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;

public class CalculatorTest {

    @Test
    public void add() {
        // given
        final Calculator calculator = new Calculator();

        // when
        final Integer result = calculator.add(2, 5);

        // then
        assertNotNull("result is null", result);
        assertEquals("result should be equal to 7 (2 + 5 = 7)", 7, result); 
    }
}

If an expected result of a test is to throw an exception, then you should set an expected parameter in the @Test:

// imports

public class CalculatorTest {

    // ...

    @Test(expected = IllegalArgumentException.class)
    public void addWhenBothAreNull() {
        // given
        final Calculator calculator = new Calculator();

        // when
        calculator.add(null, null);
    }
}

If you need to prepare some data before tests and do some cleanup after, then you can configure it by org.junit.Before and org.junit.After annotations.

// imports

public class PlayerTest {

    private MusicAnalyser analyser;

    @Before
    public void setup() {
        analyser = new MusicAnalyser();
        analyser.on();
    }

    @After
    public void cleanup() {
        analyser.off();
    }

    @Test
    public void analyse() {
        // given
        final Track track = new Track("hybris-anthem.mp3");

        // when
        // analyser is created and turned on
        final Quality result = analyser.analyse(track);

        // given
        assertEquals(Quality.THE_BEST, result);
        // after this line analyser will be turned off
    }
}

Instance variables defined in test classes are shared by all tests, so you should not modify those objects if they are not set again before next test. There is no warranty for the order of the tests.