VOOZH about

URL: https://www.javacodegeeks.com/2025/09/test-everything-advanced-unit-and-integration-testing-with-junit-5.html

⇱ Test Everything: Advanced Unit and Integration Testing with JUnit 5 - Java Code Geeks


Software testing has evolved far beyond the simple “write a test for each function” mindset. With modern architectures—microservices, cloud deployments, and distributed systems—applications require robust testing strategies that go deeper than unit tests. This is where JUnit 5 shines.

JUnit 5 is not just an update of JUnit 4; it’s a complete redesign that embraces modularity, extensibility, and advanced features for both unit and integration testing. Let’s explore how to master it.

Unit vs. Integration Testing: The Foundation

Before diving into code, let’s clarify the difference.

AspectUnit TestingIntegration Testing
ScopeIndividual methods or classesMultiple components/modules working together
DependenciesMocked/stubbedReal or semi-real (e.g., database, API)
SpeedVery fastSlower due to environment setup
Tools in JUnit 5Assertions, parameterized tests, mocks (Mockito)Testcontainers, Spring Test, real services

In practice, a balanced strategy includes both—unit tests for correctness and integration tests for system reliability.

Advanced Unit Testing with JUnit 5

Nested Tests for Structure

JUnit 5 introduces @Nested tests, allowing you to organize related test cases more clearly.

import org.junit.jupiter.api.*;

class CalculatorTest {

 @Nested
 @DisplayName("Addition Tests")
 class Addition {
 @Test
 void shouldAddTwoNumbers() {
 Assertions.assertEquals(5, 2 + 3);
 }
 }

 @Nested
 @DisplayName("Subtraction Tests")
 class Subtraction {
 @Test
 void shouldSubtractTwoNumbers() {
 Assertions.assertEquals(1, 3 - 2);
 }
 }
}

This improves readability, especially in larger projects.

Parameterized Tests

Avoid repetitive test code by using @ParameterizedTest.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class PalindromeTest {
 @ParameterizedTest
 @ValueSource(strings = {"radar", "level", "madam"})
 void shouldIdentifyPalindromes(String word) {
 Assertions.assertTrue(new StringBuilder(word).reverse().toString().equals(word));
 }
}

Using Mocks with Mockito

Mockito integrates seamlessly with JUnit 5 for isolating units under test

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;

class UserServiceTest {
 @Test
 void shouldFetchUserById() {
 UserRepository repo = mock(UserRepository.class);
 when(repo.findById(1)).thenReturn(new User(1, "Alice"));

 UserService service = new UserService(repo);
 User user = service.getUser(1);

 Assertions.assertEquals("Alice", user.getName());
 }
}

Integration Testing with JUnit 5

Testing with Spring Boot

JUnit 5 works perfectly with Spring Boot via @SpringBootTest.

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

@SpringBootTest
class UserIntegrationTest {

 @Autowired
 private UserService userService;

 @Test
 void shouldCreateUser() {
 User user = userService.createUser("Alice");
 Assertions.assertNotNull(user.getId());
 }
}

This spins up the Spring context, enabling full-stack integration testing.

Testcontainers for Real Environments

A common pain point in integration testing is database or message queue setup. With Testcontainers, you can spin up lightweight Docker containers inside tests.

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;

class DatabaseIntegrationTest {

 @Test
 void testDatabaseConnection() {
 try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
 postgres.start();

 String jdbcUrl = postgres.getJdbcUrl();
 // connect to db and run assertions
 Assertions.assertTrue(jdbcUrl.contains("postgresql"));
 }
 }
}

This ensures integration tests run consistently across environments.

Best Practices

  1. Fast feedback: Keep unit tests lightweight and run them on every build.
  2. Layered strategy: Write more unit tests, fewer integration tests—but ensure integration covers critical flows.
  3. Use tags and filters:
@Tag("integration")
@Test
void slowTest() { ... }

Run them selectively with Maven/Gradle when needed.

4. Leverage extensions: JUnit 5’s Extension API allows custom lifecycle management, mocking, or logging.

Final Thoughts

JUnit 5 empowers developers to move beyond “just unit testing” into a world where integration testing is equally first-class. With nested tests, parameterization, dependency injection, and Testcontainers, you can build a comprehensive testing strategy that ensures both correctness and resilience.

Modern development demands: test everything. And with JUnit 5, that’s not just a mantra—it’s practical.

Further Reading

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

👁 Photo of Eleftheria Drosopoulou
Eleftheria Drosopoulou
September 22nd, 2025Last Updated: September 17th, 2025
0 508 2 minutes read

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button
Close
wpDiscuz