DEV Community

Cover image for JUnit 5 - Writing Tests
Chathumi Kumarapeli
Chathumi Kumarapeli

Posted on

JUnit 5 - Writing Tests

In JUnit 5 - Introduction we talked about JUnit and we wrote a really simple test case. In this tutorial we are going to dive in-depth to write more complex test methods.

JUnit Test Lifecycle 💫

In JUnit it is not that you only have the annotation @Test for test methods. There may be cases where you have to run a test before all the other tests. Or you might have a test method to run before executing every other test method. Or you might have a test method to run when all the other test methods have executed. Not to worry, cause JUnit covers all these scenarios 😃

There are 5 phases that a test method can have. They are as follows;

  • @BeforeAll
  • @BeforeEach
  • @Test
  • @AfterEach
  • @AfterAll

@BeforeAll

Test methods marked with @BeforeAll annotation get executed before any of the test methods that are available in the Test class.
Let's write a simple test method using above annotation.

@BeforeAll
public static void TestBeforeAll () {
    System.out.println("Should print BEFORE executing any other tests");
}

@Test
public void TestBeforeAllSuccess() {
    System.out.println("Should print AFTER executing TestBeforeAll () method");
}
Enter fullscreen mode Exit fullscreen mode

As you can see here I have added two test methods. TestBeforeAll () is with the annotation @BeforeAll and TestBeforeAllSuccess() is a normal test method. Now when we run the program TestBeforeAll () executes first. Which means your output should look like this,
2_testBeforeAll

@BeforeEach

Test methods marked with @BeforeEach annotation get executed before each of the test methods include in the Test class.

If you can remember in both test methods TestAddEmployee() and throwRuntimeExceptionWhenFirstNameIsNull(), I used the line,
ManageEmployees employees = new ManageEmployees();
Hence, there is repetition of same code line. We can avoid this by running the above line inside a new test method using the annotation @BeforeEach. Which mean this line will execute before the execution of each and every tests available in the test class.

ManageEmployees employees;

@BeforeAll
public static void TestBeforeAll () {
    System.out.println("Should print BEFORE executing any other tests");
}

@BeforeEach
public void TestBeforeEach() {
    ManageEmployees employees = new ManageEmployees();
    System.out.println("Execute BEFORE EACH test method");
}

@Test
public void TestBeforeAllSuccess() {
    System.out.println("Should print AFTER executing TestBeforeAll () method");
}
Enter fullscreen mode Exit fullscreen mode

In the above code I have added print statements for you to easily understand the order of test method execution 😁 Check the figure below to see the execution order of test methods.
3_testBeforeEach

@test

Yes, you know this annotation since we used it in the previous tutorial 😎 But let's try out a bit complex one 🤯 What about writing a test to validate inputs? That means you know that we don't let to keep any attribute (first_name, last_name, or contact_number) empty. We can write separate test methods to throw exceptions when any of the above fields is empty.

@Test
@DisplayName("Object should not create when first_name is null")
public void throwRuntimeExceptionWhenFirstNameIsNull() {
    ManageEmployees employees = new ManageEmployees();
    Assertions.assertThrows(RuntimeException.class, () -> {
        employees.addEmployee(null, "Cullen", "0123456789");
    });
    System.out.println("Number of Employees: " + employees.getEmployees().size());
}
Enter fullscreen mode Exit fullscreen mode

According to the above code, it throws a Runtime Exception when you try to create an object with an empty first name and do not create the object. I added the print statement so that you can clearly see that the object is not created. Look at the terminal below.

14_testTest

You can see that the number of employees is zero, which means that object is not created just like we expected 🙂

The @DisplayName in above code is a custom name that you can provide for a test method to increase the readability apart from the test method name.

Now I have a task for you 🥴 Try to write test methods by yourself to throw exceptions when last name and contact number is given as empty. Don't panic, it's very simple. You got this 😌

@AfterEach

Test methods marked with @AfterEach annotation get executed after each test method inside the Test class. Look at the two test methods I have added.

@Test
public void TestAfterEachSuccess() {
    System.out.println("Should print BEFORE executing TestAfterEach () method");
}

@AfterEach
public void TestAfterEach() {
    System.out.println("Should print AFTER the execution of EACH test method.");
}
Enter fullscreen mode Exit fullscreen mode

Now let's run and see how the test methods execution happens.

4_testAfterEach

As you can see TestAfterEach() executes after the execution of each and every test method.

@AfterAll

Test methods marked with @AfterAll annotation get executed after all the test methods in the Test class are finished executing.

 @AfterAll
public static void TestAfterAll() {
    System.out.println("Should execute AFTER ALL the test methods");
}
Enter fullscreen mode Exit fullscreen mode

5_testAfterAll

As you can see the test method TestAfterAll() has executed after all the other test methods are executed.

Those are the five phases of Test Lifecycle. The main use of this lifecycle is to perform initialization tasks or cleanup tasks.

  1. @BeforeAll and @BeforeEach: to perform initialization tasks for tests.
  2. @AfterEach and @AfterAll: to perform cleanup tasks for tests.

With that we came to the end of this tutorial. You can learn how to write repeatable tests and conditional tests in the next tutorial 😎

Top comments (0)