Testing in Java: unit, integration, e2e from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why is testing needed in Java?

\n

Every Java developer has at least once encountered a situation where changing a single line of code "broke" the entire system. To avoid such disasters, the industry has developed a coherent testing system. In this article, we will cover three levels of testing in Java: unit, integration, and e2e (end-to-end). You will learn how they differ, what tools to use, and how to write tests in Java correctly.

\n

Whether you are writing microservices on Spring Boot or desktop applications, understanding the testing pyramid is an essential skill. Let's go!

\n\n

1. Unit Testing: The Foundation of Quality

\n

Unit tests verify the operation of individual classes or methods in isolation from external dependencies. This is the fastest and cheapest level of testing.

\n\n

1.1 Main Tools

\n
    \n
  • JUnit 5 — the standard framework for Java (latest version is Jupiter).
  • \n
  • Mockito — a library for creating mock objects (stubs).
  • \n
  • AssertJ — a library with a rich set of fluent assertions.
  • \n
\n\n

1.2 Example of a Unit Test with JUnit 5 and Mockito

\n

Suppose we have a service UserService that depends on a repository UserRepository:

\n
public class UserService {\n    private final UserRepository repository;\n\n    public UserService(UserRepository repository) {\n        this.repository = repository;\n    }\n\n    public String getUserName(Long id) {\n        User user = repository.findById(id);\n        return user != null ? user.getName() : "Unknown";\n    }\n}
\n

Let's write a unit test, isolating the external dependency via Mockito:

\n
import org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\nclass UserServiceTest {\n\n    @Mock\n    private UserRepository userRepository;\n\n    @InjectMocks\n    private UserService userService;\n\n    @Test\n    void testGetUserName_WhenUserExists() {\n        // Arrange\n        User mockUser = new User(1L, "Alice");\n        when(userRepository.findById(1L)).thenReturn(mockUser);\n\n        // Act\n        String result = userService.getUserName(1L);\n\n        // Assert\n        assertEquals("Alice", result);\n    }\n\n    @Test\n    void testGetUserName_WhenUserNotFound() {\n        when(userRepository.findById(99L)).thenReturn(null);\n\n        String result = userService.getUserName(99L);\n\n        assertEquals("Unknown", result);\n    }\n}
\n

Best practice: one test — one assertion. Use clear method names (e.g., testGetUserName_WhenUserExists).

\n\n

2. Integration Testing: Verifying Interactions

\n

Integration tests verify how system components work together: database, file system, external APIs. They are slower than unit tests but provide more confidence in the correctness of the connections.

\n\n

2.1 Tools for Integration Testing in Java

\n
    \n
  • Spring Boot Test — @SpringBootTest for loading the application context.
  • \n
  • Testcontainers — running real databases (PostgreSQL, MySQL) in Docker containers.
  • \n
  • H2 — in-memory database for fast tests.
  • \n
\n\n

2.2 Example of an Integration Test with Spring Boot and Testcontainers

\n
import org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.t                

Blogs

Book Recommendations