Chapter 4: Integration Testing with Spring Boot


This chapter focuses on how to perform integration testing in Spring Boot applications using tools like Spring Boot Test and Rest Assured. It includes practical examples, best practices, a hands-on lab, and a quiz to reinforce learning.


Objective

By the end of this chapter, you will:

  • Understand the purpose and importance of integration testing in software development.
  • Learn how to use Spring Boot Test to load the application context for integration testing.
  • Test REST APIs with Rest Assured to validate HTTP interactions.
  • Verify interactions between components (e.g., controllers, services, and repositories).
  • Apply best practices for writing effective integration tests.

4.1 Introduction to Integration Testing

Integration testing bridges the gap between unit testing and system testing, ensuring that different parts of an application work together correctly.

  • Definition: Integration testing verifies the interactions between integrated components or modules of a system to ensure they function as a cohesive unit.
  • Purpose:
    • Detect issues that arise when components are combined (e.g., mismatched data formats, incorrect method calls).
    • Validate end-to-end functionality of specific workflows or features.
    • Ensure dependencies (like databases or external services) integrate seamlessly with the application.
  • Key Characteristics:
    • Scope: Tests multiple components together, unlike unit testing’s focus on isolation.
    • Realism: Often uses real dependencies (e.g., an in-memory database) instead of mocks.
    • Complexity: More complex and slower than unit tests due to broader coverage.

Think of integration testing as assembling puzzle pieces—each piece (unit) might be perfect on its own, but they need to fit together properly.


4.2 Tools for Integration Testing in Spring Boot

Spring Boot provides excellent tools for integration testing, complemented by libraries like Rest Assured for API testing.

  • Spring Boot Test: A module that simplifies integration testing by providing utilities to load the application context, configure test environments, and manage dependencies.
  • Rest Assured: A Java library for testing RESTful APIs, offering a fluent API to send HTTP requests and validate responses.
  • H2 Database: An in-memory database often used in integration tests to simulate a real database without external setup.

These tools allow you to test Spring Boot applications in a controlled yet realistic environment.


4.3 Setting Up Integration Tests in Spring Boot

Integration tests require a test environment that mimics the real application setup.

4.3.1 Example Scenario

Consider a Spring Boot application with a BookController, BookService, and BookRepository:

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;

    // Constructors, getters, setters
}

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {}

@Service
public class BookService {
    private final BookRepository bookRepository;

    @Autowired
    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public Book createBook(String title, String author) {
        Book book = new Book(title, author);
        return bookRepository.save(book);
    }

    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }
}

@RestController
@RequestMapping("/books")
public class BookController {
    private final BookService bookService;

    @Autowired
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @PostMapping
    public ResponseEntity<Book> createBook(@RequestBody Book book) {
        Book createdBook = bookService.createBook(book.getTitle(), book.getAuthor());
        return ResponseEntity.ok(createdBook);
    }

    @GetMapping
    public ResponseEntity<List<Book>> getAllBooks() {
        return ResponseEntity.ok(bookService.getAllBooks());
    }
}

4.3.2 Dependencies

Add these dependencies to your pom.xml (Maven) or build.gradle (Gradle):

<!-- Maven -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

4.4 Writing Integration Tests with Spring Boot Test

Spring Boot Test loads the application context, allowing you to test components in a realistic setup.

4.4.1 Testing the Service and Repository

For BookService, we test its interaction with the repository using an in-memory database (H2).

Test Code:

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class BookServiceIntegrationTest {
    @Autowired
    private BookService bookService;

    @Autowired
    private BookRepository bookRepository;

    @Test
    public void testCreateBook() {
        // Act
        Book book = bookService.createBook("Test Title", "Test Author");

        // Assert
        assertEquals("Test Title", book.getTitle());
        assertEquals("Test Author", book.getAuthor());
        assertEquals(1, bookRepository.count());
    }

    @Test
    public void testGetAllBooks() {
        // Arrange
        bookRepository.save(new Book("Book1", "Author1"));
        bookRepository.save(new Book("Book2", "Author2"));

        // Act
        List<Book> books = bookService.getAllBooks();

        // Assert
        assertEquals(2, books.size());
        assertEquals("Book1", books.get(0).getTitle());
    }
}

Explanation:

  • @SpringBootTest: Loads the full application context, including beans like BookService and BookRepository.
  • H2: Automatically configured as an in-memory database for testing.
  • Assertions: Verify the service saves and retrieves books correctly through the repository.

4.5 Testing REST APIs with Rest Assured

For REST endpoints, Rest Assured lets you send HTTP requests and validate responses.

4.5.1 Testing the createBook Endpoint

Test Code:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BookControllerIntegrationTest {
    @LocalServerPort
    private int port;

    @BeforeEach
    public void setup() {
        RestAssured.port = port;
    }

    @Test
    public void testCreateBook() {
        given()
            .contentType(ContentType.JSON)
            .body("{\"title\":\"Test Title\",\"author\":\"Test Author\"}")
        .when()
            .post("/books")
        .then()
            .statusCode(200)
            .body("title", equalTo("Test Title"))
            .body("author", equalTo("Test Author"));
    }
}

Explanation:

  • @SpringBootTest(webEnvironment = RANDOM_PORT): Starts the application on a random port for testing.
  • @LocalServerPort: Captures the port to configure Rest Assured.
  • Rest Assured: Sends a POST request and verifies the response status and body.

4.5.2 Testing the getAllBooks Endpoint

Test Code:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BookControllerIntegrationTest {
    @LocalServerPort
    private int port;

    @Autowired
    private BookRepository bookRepository;

    @BeforeEach
    public void setup() {
        RestAssured.port = port;
        bookRepository.deleteAll();
        bookRepository.save(new Book("Book1", "Author1"));
        bookRepository.save(new Book("Book2", "Author2"));
    }

    @Test
    public void testGetAllBooks() {
        when()
            .get("/books")
        .then()
            .statusCode(200)
            .body("size()", is(2))
            .body("[0].title", equalTo("Book1"))
            .body("[1].title", equalTo("Book2"));
    }
}

Explanation:

  • Setup: Prepares test data in the database before each test.
  • Rest Assured: Sends a GET request and checks the response contains the expected books.

4.6 Best Practices for Integration Testing

To ensure your integration tests are effective, follow these guidelines:

  • Use Realistic Scenarios: Test workflows that mirror real-world usage.
  • Minimize Dependencies: Use in-memory databases or mock external services when possible to avoid external failures.
  • Clean Up Data: Reset the test environment (e.g., clear the database) before or after each test to avoid interference.
  • Focus on Key Integrations: Test critical interactions (e.g., controller-service-repository) rather than every possible combination.
  • Balance with Unit Tests: Keep integration tests focused on integration points, leaving detailed logic checks to unit tests.

4.7 Lab: Hands-On Integration Testing with Spring Boot

Apply your knowledge in this practical lab.

Lab Objective

Write integration tests for a provided Spring Boot REST API using Spring Boot Test and Rest Assured.

Lab Setup

  • Provided: The same BookController, BookService, and BookRepository from earlier.
  • Tools: Maven/Gradle with spring-boot-starter-test, h2, and rest-assured.

Lab Tasks

  1. Test the Service Layer:

    • Write an integration test for createBook to verify a book is saved to the database.
    • Test getAllBooks to ensure it retrieves all books.
  2. Test the Controller Layer:

    • Write an integration test for the createBook endpoint using Rest Assured to confirm a POST request works.
    • Test the getAllBooks endpoint to verify it returns the correct list of books.
  3. Ensure Data Integrity:

    • Add setup or cleanup logic to ensure tests don’t interfere with each other.

Lab Deliverables

  • Submit your test classes with all integration tests.
  • Include comments explaining the purpose of each test.

4.8 Quiz: Reinforcing Key Concepts

Test your understanding with this quiz:

  1. What is the primary focus of integration testing?

    • a) Testing individual methods in isolation
    • b) Verifying interactions between components
    • c) Checking user interfaces
    • d) Validating performance
  2. What does Spring Boot Test provide for integration testing?

    • a) A mocking framework
    • b) An application context and test utilities
    • c) A browser automation tool
    • d) A database simulator
  3. Which library is used to test REST APIs in this chapter?

    • a) Mockito
    • b) JUnit
    • c) Rest Assured
    • d) H2
  4. True or False: Integration tests should always use a real external database.

    • a) True
    • b) False
  5. What annotation starts a Spring Boot application on a random port for testing?

    • a) @SpringBootTest(webEnvironment = RANDOM_PORT)
    • b) @WebMvcTest
    • c) @Test
    • d) @MockBean

Answers:

  1. b
  2. b
  3. c
  4. b (In-memory databases are often preferred)
  5. a

4.9 Summary

In this chapter, you’ve learned:

  • The importance of integration testing for verifying component interactions.
  • How to use Spring Boot Test to set up and run integration tests.
  • Techniques for testing REST APIs with Rest Assured.
  • Best practices for creating reliable and focused integration tests.

These skills enable you to ensure your Spring Boot application’s components work together seamlessly, paving the way for higher-level testing.


This chapter equips you with the tools and knowledge to perform integration testing effectively in Spring Boot, ensuring robust and reliable applications!


By Wahid Hamdi