Unit Testing with JUnit5
Tags: java, testing, tutorial, beginners
Published: 2020-08-13 05:35:04

Unit testing is a type of software testing in which we test individual software components. It is done during the development of an application by the developers. It isolates a section of code and verify its correctness. A unit may be an individual function, method, procedure, module, or object. 

Well here are a few advantages of Unit testing.

- If unit tests are well-written, then with every build of our software with changed of code, we can notice any failures when we introduce new features.
- It makes your code more reusable, as, for good unit tests, code components should be modular.
- It acts like documentation as it describes the expected behavior for a piece of code to other developers.


Now that we have defined unit testing and why we use it, we are ready to move on to JUnit5.

JUnit is one of the most popular unit-testing frameworks in Java. JUnit5 is composed of several different modules from three different sub-projects namely JUnit Platform, JUnit Vintage, JUnit Jupiter.

#### Install JUnit5

For Maven, add the dependancy to Pom.xml:
```
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>{$version}</version>
    <scope>test</scope>
</dependency>
```

For Gradle, add to build.gradle:
```
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '$version'
```

#### JUnit Annotations

Annotations are the meta-tags that provides additional information about the methods and classes defined in our code structure.

Some commonly used annotations are listed here.

- `@Test` - Represents a test method.
- `@BeforeEach` - Represents the annotated method should be executed before each test method.
- `@AfterEach` - Represents the annotated method should be executed after each test method.
- `@BeforeAll` - Represents the annotated method should be executed before all test methods.
- `@AfterAll` - Represents the annotated method should be executed after all test methods.
- `@DisplayName` - Creates a custom display name for the test class or method.
- `@Tag` - Creates tags for filtering tests.
- `@ExtendWith` - Registers custom extensions.

```
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class JUnit5Tests {

    @BeforeAll
    static void beforeAllTest() {
        log.info("Executed before all test methods.");
    }

    @BeforeEach
    void beforeEachTest() {
        log.info("Executed before each test method.");
    }

    @Test
    void test() {
        log.info("Executed test method.");
    }

    @AfterEach
    void afterEachTest() {
         log.info("Executed after all test methods");
    }

    @AfterAll
    static void afterAllTest() {
         log.info("Executed after all test methods");
    }
}
```

####JUnit Assertions

Every test method must be evaluated against the condition to true using assertions so that the test can continue to execute. JUnit Jupiter assertions are kept in the `org.junit.jupiter.api.Assertions` class. 

Here are some commonly used Assertions.

- `assertNull(actual value)` - Fails when actual value is not null.
- `assertNotNull(actual value)` - Fails when actual value is null.
- `assertEquals(expected value, actual value)` - Fails when expected value does not equal actual value.
- `assertAll()` - Groups a bunch of assertions and every assertion is executed even if one or more of them fails.
- `assertTrue(expression value)` - Fails if expression is not true.
- `assertFalse(expression value)` - Fails when the expression is not false.

```
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;

import example.io.util.Customer;

class JUnit5Tests {

    private final Customer customer = new Customer("Ann", "Marie");

    @Test
    void standardAssertions() {
        assertEquals("Marie", customer.getLastName());
        assertNotNull(customer);
        assertTrue("Ann", customer.getFirstName());
    }

    @Test
    void groupedAssertions() {
        assertAll("customer",
         () -> assertEquals("Ann", customer.getFirstName()),
         () -> assertEquals("Marie", customer.getLastName())
        );
    }
}
```

####JUnit5 Assumptions

Assumptions are static methods present inside the `org.junit.jupiter.api.Assumptions` class. These methods will execute a test only when the specified condition met otherwise the test will be aborted. The aborted test will not cause a build failure. Assumptions also understand lambda expressions.

Here are some commonly used Assumptions.

- `assumeTrue()` - Execute the body when the positive condition hold else test will be skipped.
- `assumeFalse()` - Execute the body when the negative condition hold else test will be skipped.
- `assumingThat()` - Part of the test method will execute if an assumption holds true and everything after that will execute irrespective of the assumption in assumingThat() holds.

```
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class JUnit5Tests {

    private final Calculator calculator = new Calculator();

    @Test
    void simpleAssumeTest() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test");
    }

    @Test
    void complexAssumeTest() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                assertEquals(21, calculator.add(14, 7));
            });
     }
}
```

####Further Reading
- To get more insights and deeper understanding refer to the [User Guide of JUnit5](https://junit.org/junit5/docs/current/user-guide/). 
- Starter example project for Maven, check out the [junit5-jupiter-starter-maven](https://github.com/junit-team/junit5-samples/tree/r5.6.2/junit5-jupiter-starter-maven) project.
- A basic understanding guide from [JUnit5 Tutorials](https://mkyong.com/junit5/junit-5-tutorials/).

We have covered JUnit5 and a few of its features with some examples. We also looked at how we can use JUnit annotations, assertions, assumptions. 

Thanks for reading. Happy Learning. :heart: