Beginners Guide to Unit Testing with Spring Boot - spring-boot-in-practice/repo GitHub Wiki

Introduction

In this article, we'll learn to write tests with Spring Boot. Traditionally we have used JUnit to write unit tests in Java. Besides, there are various mocking libraries such as Mockito, EasyMock, etc, that allows you to mock application components and assist in writing test cases.

In this article, we'll focus on writing tests in a Spring Boot application. You'll explore the spring-boot-starter-test dependency and see how it assists you to write test cases in a Spring Boot application.

If you would like to know more about testing, and the best practices, please refer to this link.

Project Set up

To write tests in a Spring Boot application, you need the following Maven dependency in your application:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

If you use Spring Initializr to generate your Spring Boot project, then you have this dependency added as default.

Examining Spring Boot Starter Test Dependency

As you have already learned in Spring Boot In Practice book, Spring Boot packages all necessary components of an area of application development with these starter dependencies. As the name indicates, this dependency packages all necessary components Spring Boot thinks you need to developed tests in a Spring Boot application.

If you explore the dependency in detail, you'll notice it has the following dependencies packaged:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-test</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-test-autoconfigure</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.jayway.jsonpath</groupId>
      <artifactId>json-path</artifactId>
      <version>2.4.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>2.3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.16.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest</artifactId>
      <version>2.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.6.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.vintage</groupId>
      <artifactId>junit-vintage-engine</artifactId>
      <version>5.6.2</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>hamcrest-core</artifactId>
          <groupId>org.hamcrest</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>3.3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-junit-jupiter</artifactId>
      <version>3.3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.skyscreamer</groupId>
      <artifactId>jsonassert</artifactId>
      <version>1.5.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.xmlunit</groupId>
      <artifactId>xmlunit-core</artifactId>
      <version>2.7.0</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>jaxb-api</artifactId>
          <groupId>javax.xml.bind</groupId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

It packages several notable testing libraries such as JUnit, Spring Test, AssertJ, Hamcrest, Mockito, XMLUnit, etc. In the book, you'll notice, we've used AssertJ extensively to assert various test scenarios. This library has a fluent API and better readability.

Default Test Case

When you generate a Spring Boot project with Spring Initializr, you might have seen Spring Boot packages an empty test case in the src\main\test folder.

package com.manning.sbip.ch03;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
class CourseTrackerSpringBootApplicationTests {
	
    @Test
    void contextLoads() {
    }
}

The important annotation to note here is @SpringBootTest.

Unit Test vs Integration Test

Before proceeding further on the technicalities of testing with Spring Boot, and how things works, let us understand the difference between the Unit test and the Integration test:

  • Unit Test: A Unit test typically focuses on a _unit _of the code. It is mostly limited to a method or to a set of methods in a class
  • Integration Test: An Integration test generally targets a specific feature of the application which often includes a set of classes that supports the feature

Understanding @SpringBootTest

Spring Boot provides this annotation which can be used to create an ApplicationContext instance that contains all beans to perform the integration test.

In general, a unit test should be short and finish its execution quickly. Thus, if you are testing only a unit of your code, it is recommended to use mocking libraries to mock and perform the test. Using @SpringBootTest can lead to long-running test cases as it creates the ApplicationContext and perform several other additional activities (e.g. Register a TestRestTemplate, or a WebTestClient) to prepare the environment for integration testing.

⚠️ **GitHub.com Fallback** ⚠️