Ref

Let’s get started with Java Testing Framework : JUnit 5

Amol Jadhav
Globant
Published in
7 min readOct 18, 2021

--

The higher your test coverage, the less code failures.

You may not write successful production code until you have written a failing unit test

Catching mistakes in the development phase is a lot less costly than finding them in production. It not only saves you from embarrassment, but it also saves your company and user from frustration.

The reason is simple. If you have tests, you do not fear making changes to the existing code! Without tests, every change is a possible bug.

No matter how flexible your architecture is!

No matter how nicely partitioned your design!

Without tests, you will be reluctant to make changes because of the fear that you will introduce undetected bugs.

Stick to the Basics : What is Unit testing ?

Unit test is at the lowest level and typically checks a single method to confirm that they returns the intended result based on condition

Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the “unit”) meets its design and behaves as intended.
So, we can explain that Unit Testing is to make sure how well our code or every unit on our source code runs, based on the business process which is already defined.

Lets make our hand dirty to write Clean Test Cases :

Photo by home stratosphere

First thing First : Adding JUnit 5 dependencies

  • The junit-jupiter-api provides the public API for writing tests cases
Ref Maven Repository

Problem Statement:
Find the age as per given birth date (Day, Month, Year).

Constraint :
Birth date should be before today’s date

To write a simple JUnit test case, we need to declare a class in test package and implement a methods with@Test annotation
The return type of test method is always void and you need to verify expected and actual results to make the test pass or fail.

Test Case 1: Success Scenario

Input: Birth Date : 15th Aug, 1992
Expected output Age : 29 years

Success Scenario Test Case

Test Case 2: Fail Scenario

Input: Birth Date : 15th Aug, 2022
Expected output: throw InvalidAgeException with message “Invalid Birth Date”

Fail Scenario Test Case

assertEquals is static method of Assertions class to check that two objects are equal, if the condition provided to assertEquals gets fail then our test case will get fail. assertThrows asserts that the supplied executable throws the exception of the expected type.

If you don’t have assertions in the test then test case always passes. We will take a look about Assertion class later.

The lifecycle of a JUnit Test

We can have a lot of test cases in each JUnit test class and each test case goes through a lifecycle as below. We have an option to set up before all the tests run and before each test. We have the option of cleaning up after all the tests and after each test.

lifecycle per-method (Default Lifecycle): For every test method with in a test class one test instance will be created.

lifecycle per-class : For every test class one test instance will be created, not per test method.

  • @BeforeEachdenotes that the annotated method will be executed before each test method (JUnit4's @Before)
  • @AfterEachdenotes that the annotated method will be executed after each test method (JUnit4's @After)
  • @BeforeAlldenotes that the annotated method will be executed before all test methods in the current class (JUnit4's @BeforeClass)
  • @AfterAlldenotes that the annotated method will be executed after all test methods in the current class (JUnit4's @AfterClass )

Let’s see the lifecycle with an implementation.

Output

Global count got incremented by once because init() method executed only once as we have annotated it with @BeforeAll.

Local Count got increment by twice because beforeEachTest()method executed by twice as we have annotated it with @BeforeEach.

JUnit 5 Assertions

JUnit 5 assertions help in validating the expected output with actual output of a testcase. To keep things simple, all JUnit Jupiter assertions are static methods in the org.junit.jupiter.Assertions class as follow:

To Assert the Boolean variables.

To verify that two objects are equal.

Note: We should always implement equals and hashCode when we want to test the equality of two custom objects, else it might not work as expected.

To test an object’s null condition

To check the equality of two arrays.

Note: We have to make sure that the elements in both the arrays are in the same order and If both the array objects are null then they are considered equal.

To verifies that two Iterables are equal with a deep comparison

Note: Both iterables should return the objects in the same order.

To test that variables do refer to the same object or not

To verifies all the multiple executables at once

Note: if any executables throws exception it will continue to run all the other executables, consolidates the exceptions and report all of them at once.

Exception Handling : To expect that executable will throws the exception of the expected type

Custom Assert : If we want to fail a test on some condition then we can use fail method.

Note : Calling this method results in AssertionFailedError.

Useful JUnit’s Annotation

@DisplayName

Test classes and test methods can declare custom display names that will be displayed by test runners and test reports.

@Disabled

The @Disabled annotation is used to disable or skip tests at class or method level. This is analogous to JUnit 4’s @Ignore.

When declared at class level, all @test methods are skipped. When we use @Disabled at the method level, only the annotated method is disabled.

@Disabled is used to signal that the annotated test method is currently disabled and should not be executed.

Dependency injection in JUnit 5

In all prior JUnit versions, test constructors or methods weren’t allowed to have parameters. As one of the major changes in JUnit 5, both test constructors and methods are now permitted to have parameters.

This allows for greater flexibility and enables dependency injection for constructors and methods.

TestInfo is used to inject information about the current test or container into test cases

Last But Not Least : Clean Test F.I.R.S.T Rule

F.I.R.S.T. clean tests follow five other rules that form the above acronym:

Fast : Tests should be fast. They should run quickly. When tests run slow, you won’t want to run them frequently.

Independent : Tests should not depend on each other. One test should not set up the conditions for the next test.

Repeatable : Tests should be repeatable in any environment. You should be able to run the tests in the DEV , QA, PROD environment without a network.

Self-Validating : The tests should have a boolean output. Either they pass or fail. You should not have to read through a log file to tell whether the tests pass.

Timely : The tests need to be written in a timely fashion. Unit tests should be written just before the production code that makes them pass.

Thank you !!!

“An investment in knowledge always pays the best interest.”
I hope this article was a good learning experience for you.
Thank you for following through with it to the end.
Be Tuned for Part 2, Mockito Framework
Happy Learning with Coding!..

--

--