Writing Unit Tests in Spring Boot

Betül Şahin
7 min readJan 2, 2022

In this post, we’ll talk about writing unit tests in Spring Boot applications. At the beginning we will give a basic introduction to the concept of testing. Then we will talk about how to write unit tests for Controller and Service classes.

Here, I shared what I learned in the Java & Spring Framework Bootcamp organized in collaboration with GittiGidiyorKodluyoruz & Patika.dev, which I attended this summer as a student.

What is Unit Test ?

Writing unit tests is an important issue that is the responsibility of the developer of the application.

Wikipedia defines unit tests as

In computer programming, unit testing is a software testing method by which individual units of source code — sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures — are tested to determine whether they are fit for use.

There are some of the different kind of tests such as unit testing and integration testing. However, integration testing is not recommended unless it is very necessary. These are tests that have the risk of exploding the system at some point.

How to Write a Test Method ?

We write our tests under the src/test folder. Directory structure guides us in this regard.

Here we create the SampleUnitTestClass class.

First, let’s create a test method as follows.

  • void — Test methods are always written as void. Because we don’t call test methods anywhere. This means that all unit tests are independent of each other.
  • @Test — We always put this annotation at the beginning of our test methods. It is in the package org.junit.jupiter.api. Spring provides the JUnit library by default. JUnit allows us to write unit tests. JUnit 5 was released along with Java 8.

To run test, click the run button to the left of the method. You will get the following output. The test result is in the lower right section.

So let’s start ! 💪

Create Calculator inner class inside SampleUnitTestClass class. And then write add() method which returns the sum of two numbers.

Now let’s write the test_add() method for this class. Test methods are written with the BDD (Behavior driven development) approach. This approach consists of given, when and then parts.

  • given — the prerequisites for the test.
  • when — the behavior that you’re specifying.
  • then — describes the changes you expect due to the specified behavior.

In the test method, we said that when we give the parameters 10 and 20 in the add(a, b) method of the Calculator class, we expect the result of 30. When you run the test you will get the following result.

As you see everything is green, our test passed!

You can observe the fail status of the test by setting the Expected value to a different value. I don’t want to prolong the article and take up your time.

Unit Test Naming Conventions

There are different types of naming conventions. In this tutorial, i shared what i learned in the bootcamp training.

@Test
public void should_returnEquals_when_addTwoNumber(){}

Here are the should and when prefixes. So we always put them in. Other parts are changing according to our method. When we read the test method, we should be understand what that test is for. We use underscores to make the method easier to read.

We can read our test method as follows: When two numbers are added together, they must be equal.

Worst Case Test

Above, we developed a test method for the best case scenario. We may encounter the opposite situation. We also need to test for cases where two numbers are not equal when added.

Now let’s write the negative case for the same example.

When you run it, you will see that the test passed. Here we said that we expect the sum of the numbers 10 and 20 not to equal 40. As a matter of fact, it does not equal 40 and our test passed.

The Basics of Unit Testing

  • If you want to run all the tests inside the test class at the same time, you should run the run command on the left of the class.
  • DisplayName() — Used to change method name.
@Test
@DisplayName(value = "This test should return equals when add two number")
public void should_returnEquals_when_addTwoNumber(){

...}
  • RepeatedTest() — Runs the same test as many times as the given parameter value.
@Test
@RepeatedTest(10)
public void should_returnNotEquals_when_addTwoNumber(){}
  • ParameterizedTest() — Used to run the same test with more than one value.

Write a new test method to examine through the example. When we multiply a number by 0, we expect the result to return 0.

Above, we given our values with ValueSource(). However, the CsvSource() annotation is used to give multiple values.

Testing Error Conditions

Executable is used for this. It’s a nice feature that released with JUnit 5. We wrap the error in the Executable interface.

We would be getting the error “java.lang.ArithmeticException: / by zero”.

In the first parameter of assertThrows() we specify the type of error to test.

  • BeforeEach — Runs before each test method. Here we can do situations like initializing a variable.

We created our calculatorTest object in the setup() method.

  • AfterEach — It is used for operations after each test. An annotation that is not used very often.
  • BeforeAll — Run before all tests. It is defined as static.
  • AfterAll — Used to free up resources after all tests. It is defined as static. These two annotations can be especially useful for integration tests.

Unit Testing for Controller and Service Layer

It is hard to write a unit test for fat methods. However it is easy to write a unit test for a more minimal methods.

First of all, I would like to briefly touch on TDD, namely test driven development. In this approach, we write the test first and then the code. In this post, my aim is to give an overview of how to write a unit test. So I will not use the TDD approach in the case study. I just wanted to mention it for giving information about it. For more detailed information, I recommend you to watch this and that videos.

Case

We have a restfull api that we publish at localhost:8080/api/v1/customers. It performs customer addition, listing and deletion operations.

Testing The Controller Layer

Press Ctrl+Shift+T while inside the CustomerController class. The CustomerControllerTest class will be created under the src/test/controllers folder.

Testing for Best Case of create() Method

First, let’s write a test for the create method.

Then we will mock the return value of customerService.create(request). For this, we will use Mockito library that comes ready in Spring.

  • @ExtendWith(MockitoExtension.class) — The annotation used for test classes.
  • @MockThe annotation used to mock the dependencies of the test class.
  • @InjectMocksThe annotation used to inject mock objects into the test class.
  • @Test The annotation used for test methods.

Now we can write our test ! 💪

In the following line of code (line 8), we are telling our test method to return the expected variable when mockCustomerService.create() is called. Thus, we have done the mock process.

  • when() We specify which method to call.
  • thenReturn() —We specify what result we expect the called method to return.
  • any() We specify that it can take any parameter of object type.
when(mockCustomerService.create(any())).thenReturn(expected);

We test our create method on the 11–13. lines. And then we assign the returned result to the variable of the actual.

From the 16th line, we compare the returned and the expected results.

assertAll(
() -> assertNotNull(actual),
() -> assertEquals(HttpStatus.CREATED, response.getStatusCode()),
() -> assertEquals(expected.get(), actual),
() -> assertEquals(customer.getIdentificationNumber(), actual.getIdentificationNumber()));
  • assertNotNull(actual) — We claim that the variable of actual will not be null. It will return true if the variable of actual is not null.
  • assertEquals(param1, param2) — We compare whether both parameters are equal to each other.

Let’s click the green run button on the left to run it. Our test passed!

Testing for Worst Case of create() Method

Now let’s test the worst case of the create method. For this, we passed a nullable Optional object to thenReturn() on line 4.

Testing for getAll() ve getById() Methods

You can access the test codes for these methods from this repository.

Testing for deleteById() Method

Let’s test the worst case of the deleteById() method.

Let’s look 4th line.

  • doThrow(param) —We specify the exception class to throw.
  • when(param).deleteById(anyLong()) — We specify that we will call the deleteById() method of our service object.

Testing The Service Layer

There are similar procedures in service tests. You can checkout it from the repository below.

--

--