As applications grow in complexity, so do their test suites. A few isolated unit tests might work for a small project, but enterprise-scale Java applications require structured, organized test classes and suites. Without proper organization, test execution becomes chaotic, debugging failures becomes harder, and CI/CD pipelines slow down.
JUnit 5 introduces a modern way to organize test classes, group related tests, and run test suites efficiently. In this tutorial, we’ll explore different strategies to structure test classes and suites, demonstrate code examples, and share best practices for scaling test efforts.
Why Organize Test Classes and Suites?
- Maintainability: Easier to understand and maintain large codebases.
- Separation of Concerns: Keep unit, integration, and E2E tests isolated.
- Scalability: Allows teams to execute subsets of tests quickly.
- CI/CD Efficiency: Run relevant tests in pipelines (e.g., unit tests on every commit, integration tests nightly).
- Collaboration: Provides clarity for multiple developers contributing to a project.
Think of organized test classes like folders in a filing cabinet — without labels and structure, finding documents becomes painful.
Structuring Test Classes in JUnit 5
Naming Conventions
- Use
*Test
or*Tests
suffixes (e.g.,UserServiceTest
). - Use descriptive method names (
shouldSaveUserCorrectly
). - Separate tests into packages like:
unit
integration
e2e
Example: Simple Test Class
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void shouldAddTwoNumbers() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
}
Nested Tests for Logical Grouping
JUnit 5 supports nested tests using @Nested
, which improves readability by grouping related test cases.
import org.junit.jupiter.api.*;
class UserServiceTest {
@Nested
class CreateUserTests {
@Test
void shouldCreateUser() {
UserService service = new UserService();
User user = service.createUser("Alice");
Assertions.assertNotNull(user);
}
}
@Nested
class DeleteUserTests {
@Test
void shouldDeleteUser() {
UserService service = new UserService();
service.deleteUser("Alice");
Assertions.assertTrue(service.isDeleted("Alice"));
}
}
}
Organizing Test Suites in JUnit 5
In JUnit 4, @RunWith(Suite.class)
was used. In JUnit 5, we use the JUnit Platform Suite API.
Example: Creating a Test Suite
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
@Suite
@SelectClasses({
CalculatorTest.class,
UserServiceTest.class
})
public class ApplicationTestSuite {
}
This suite runs CalculatorTest
and UserServiceTest
together.
Selecting Packages Instead of Classes
You can select entire packages to include all tests within them.
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;
@Suite
@SelectPackages("com.example.myapp.unit")
public class UnitTestSuite {
}
Combining Multiple Suites
JUnit 5 allows combining packages and classes in a single suite.
import org.junit.platform.suite.api.*;
@Suite
@SelectPackages({"com.example.myapp.unit", "com.example.myapp.integration"})
@SelectClasses({CalculatorTest.class, UserServiceTest.class})
public class CombinedSuite {
}
Advanced Features
- Tags: Use
@Tag("integration")
to filter tests in CI/CD pipelines. - Dynamic Tests: Generate tests programmatically for data-driven scenarios.
- Parallel Execution: JUnit 5 allows running tests concurrently for faster builds.
- Extensions: Combine with Spring’s
@SpringBootTest
for context loading in suites.
Real-World Case Studies
-
Spring Boot Applications:
Use@SpringBootTest
for integration tests, grouped separately from fast unit tests. -
Microservices:
Organize suites to run contract tests with Pact and containerized integration tests with Testcontainers. -
Legacy Codebases:
Introduce order gradually by grouping existing chaotic tests into suites.
Best Practices
- Keep test methods independent and isolated.
- Separate unit, integration, and E2E into different packages.
- Use tags and suites to control test execution in CI/CD.
- Avoid overly large suites — group logically.
- Combine JUnit 5 with Mockito and Testcontainers for real-world scenarios.
Version Tracker
- JUnit 4 → JUnit 5: Suite API migrated from
@RunWith
to@Suite
and@Select*
annotations. - Mockito Updates: Inline mocking support makes suites easier to maintain.
- Testcontainers Growth: Docker Compose integration supports suite-level container orchestration.
Conclusion & Key Takeaways
Organizing test classes and suites in JUnit 5 is about clarity, maintainability, and efficiency. By leveraging nested tests, tagged execution, and suite APIs, developers can structure test suites that scale with project growth and CI/CD demands.
Key Takeaways:
- Structure tests into packages (
unit
,integration
,e2e
). - Use
@Nested
for logical grouping inside test classes. - Create reusable suites with
@Suite
,@SelectClasses
, and@SelectPackages
. - Use tags to control CI/CD execution.
FAQ
1. What replaced @RunWith(Suite.class) in JUnit 5?
The new JUnit Platform Suite API with @Suite
and selectors.
2. Can I group tests without creating suites?
Yes, by using @Nested
classes and @Tag
annotations.
3. How do I run only integration tests?
Use @Tag("integration")
and configure Maven/Gradle filters.
4. Can I run multiple suites in parallel?
Yes, JUnit 5 supports parallel test execution.
5. How do I organize tests in a Spring Boot project?
Separate unit tests (src/test/java
) and integration tests (src/integrationTest/java
).
6. Can I use assumptions in suites?
Yes, assumptions skip tests conditionally even in suites.
7. How do I integrate Mockito with JUnit 5 suites?
Use @ExtendWith(MockitoExtension.class)
in test classes within suites.
8. Can Testcontainers be used in suites?
Yes, you can start containers in @BeforeAll
and reuse them across tests.
9. Do nested classes work inside suites?
Yes, nested test classes are fully supported in JUnit 5.
10. Should I create one large suite or multiple small ones?
Prefer multiple smaller, logically grouped suites for maintainability.