Getting Started Testing with JUnit 5: Part 1

Aziz Kale
Javarevisited
Published in
6 min readSep 15, 2023

One of the key reasons why the Spring Framework is so popular is its strong support for testing. You can test your application thoroughly without having to deploy it to a live server or use a real database. This helps you ensure that each part of your software works as expected.

The Spring Test Context Framework, often called “Spring Test,” is crucial for this. It loads the application context of your Spring-based application, making it available for testing in your test classes. This means you can test different parts of your Spring application, like services and database connections, without needing a real server or database. It makes tests run faster and more reliably.

Additionally, the Spring Test Context Framework offers various test modes and configurations. This flexibility allows you to test your application in different scenarios, giving you more options to check the strength and correctness of your Spring-based applications.

I use IntelliJ IDEA as the IDE and it will be a Maven project

To enable Spring Test in your project, you only need to include it as a dependency in your project’s pom.xml file.

First, I am creating the project using the link start.spring.io as shown below:

I added no dependency here, and I am adding it manually:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>

And I am adding the class Mathsat the path: com/azizkale/junittestingproject/Maths.java

package com.azizkale.junittestingproject;

public class Maths {
public int add(int p1, int p2){
return p1 + p2;
}

public int sub(int p1, int p2){
return p1 - p2;
}
}

Now the test class and the first test method come at the path: src/test/java/com/azizkale/junittestingproject/MathsTest.java

package com.azizkale.junittestingproject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class MathsTest {
@Test
public void additionTest() {
// GIVEN
Maths maths = new Maths();
int num1 = 4;
int num2 = 6;

// WHEN
int addition = maths.add(num1, num2);

// THEN
assertEquals(10, addition);
System.out.println("the result is: " + addition);
}
}

To run the test functions you can use the button at the left side of the function in IntelliJ IDEA.

“Given-When-Then” is a common structure used in software testing to describe and organize test cases. It helps clarify the different aspects of a test case. Here’s a short explanation of each part:

  1. GIVEN: Arranging variables or classes, if needed
  2. WHEN: Acting of method
  3. THEN: Result

additionTest method is a JUnit test method that I created.

The assertEquals is an assertion in a unit test. An assertion is a statement in a test that checks whether a specific condition is true or false. In this case, it is verifying whether the value of the additon variable is equal to 10.

Things to be careful about when writing unit tests:

  • The smallest piece is tested, and there should only be one.
  • Each test method should contain only one scenario.

Let’s try to understand this with a negative example using subtraction.

@Test
public void testSubtraction(){
Maths maths = new Maths();

assertEquals(2,maths.sub(8,6));
assertEquals(7,maths.sub(7,0));
}

This test will pass successfully. However, the potential problem that may be encountered is that if the first test fails, the subsequent tests will not be executed.

  • GWT (Given-When-Then) pattern and using different method names can lead to cleaner code.
  • Descriptive method names that reflect the Given-When-Then stages can make the code more understandable.
  • Each test method should be able to run independently from others.

Therefore, I rearrange the test function:

@Test
public void testSubtraction(){
// GIVEN
Maths maths = new Maths();
int num1 = 4;
int num2 = 6;

// WHEN
int varSub = maths.sub(num1, num2);

// THEN
assertEquals(-2, varSub);
System.out.println("the result is: " + varSub);
}

4 useful annotations of JUnit 5

The annotations are:

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

@BeforeAll and @AfterAll are used for one-time setup and teardown tasks that should run before and after all test methods in the class. @BeforeEach and @AfterEach are used for setup and teardown tasks that should run before and after each test method.

Let’s try to understand how these annotations work with a few simple examples. After adding these annotations and their related functions, my test class appears as follows:

package com.azizkale.junittestingproject;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class MathsTest {

private Maths maths;

// This method runs once before any test methods in this class
@BeforeAll
public static void setUpBeforeClass() {
System.out.println("Before all tests");
}

// This method runs once after all test methods in this class
@AfterAll
public static void tearDownAfterClass() {
System.out.println("After all tests");
}

// This method runs before each test method
@BeforeEach
public void setUp() {
maths = new Maths();
System.out.println("Before each test");
}

// This method runs after each test method
@AfterEach
public void tearDown() {
maths = null;
System.out.println("After each test");
}

@Test
public void additionTest() {
// GIVEN
int num1 = 4;
int num2 = 6;

// WHEN
int addition = maths.add(num1, num2);

// THEN
assertEquals(10, addition);
System.out.println("the result is: " + addition);
}

@Test
public void testSubtraction(){
// GIVEN
int num1 = 4;
int num2 = 6;

// WHEN
int varSub = maths.sub(num1, num2);

// THEN
assertEquals(-2, varSub);
System.out.println("the result is: " + varSub);
}
}

We can follow the results on the console in order to see how they work:

As seen above, the functions with@BeforeAll and @AfterAll annotations have run only one time. And others have run after and before all test methods. Hence, there is no need to instantiate the Mathsclass for each function. I declared it globally and created a single instance within the setUp()function using the @Beforeannotation.

And we can see in this example a pretty useful method of JUnit. The methodassertEquals(). It is a commonly used assertion in testing frameworks like JUnit. It checks if two values or objects are equal, providing a simple way to validate expected outcomes in your tests. If the values are not equal, the test will fail, indicating that something unexpected has occurred during execution. This assertion is essential for verifying that your code behaves as intended.

The Parameter Annotation

@Parameter annotation is used in JUnit tests to create parameterized tests. This annotation allows you to run the same test method multiple times with different parameter values. It's useful for testing various scenarios.

Firstly, I’m adding a class with the name Square at the path src/main/java/com/azizkale/junittestingproject/Square.java:

package com.azizkale.junittestingproject;

public class Square {
int side;
public int calculateArea(int side){
int area = side * side;
return area;
}
}

Ant the test class at the path:

src/test/java/com/azizkale/junittestingproject/SquareAreaCalculatingTest.java:

package com.azizkale.junittestingproject;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class SquareAreaCalculatingTest {

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3, 4, 5 }) // Different side lengths
public void testCalculateSquareArea(int sideLength) {
Square square = new Square();
int expectedArea = sideLength * sideLength;
int calculatedArea = square.calculateArea(sideLength);
assertEquals(expectedArea, calculatedArea);
}
}

In the example above, the @ParameterizedTest annotation, combined with @ValueSource, allows us to run the testCalculateSquareArea method with various side lengths. For each different side length, an expected area is calculated and compared with the result from the method. This allows us to test multiple scenarios with a single test method.

When you run the test, you will obtain the following result:

Conclusion

In this article, we have aimed to introduce you to JUnit and have supported our explanations with examples. We discussed key JUnit annotations like ‘@BeforeAll,’ ‘@AfterAll,’ ‘@BeforeEach,’ and ‘@AfterEach,’ which will help you structure your test classes more efficiently and achieve successful test runs. With this knowledge, you can enhance the reliability of your software and ensure its smooth operation when utilizing JUnit.

You can access the project at this link.

You can find the second part of the topic at this link.

--

--

Aziz Kale
Javarevisited

Highly Motivated, Passionate Full Stack Developer | EMM-IT Co. | Web: azizkale.com