Comprehensive testing examples covering unit, integration, and architectural testing.
╱ E2E ╲ Few — Selenium, Playwright (see guide below)
╱─────────╲
╱ Integration╲ Some — TestContainers, REST Assured (*IT.java)
╱───────────────╲
╱ Unit Tests ╲ Many — JUnit 5, Mockito, AssertJ (*Test.java)
╱─────────────────────╲
╱ Architecture Tests ╲ Always — ArchUnit (runs with unit tests)
╱───────────────────────────╲
| File | Framework | Type |
|---|---|---|
JUnit5FeaturesTest |
JUnit 5 + AssertJ | Unit |
MockitoFeaturesTest |
Mockito | Unit |
ArchitectureRulesTest |
ArchUnit | Unit |
PostgresContainerIT |
TestContainers | Integration |
RestAssuredIT |
REST Assured + TestContainers | Integration |
# Unit tests only (fast, no Docker needed)
mvn test -pl examples/testing
# Integration tests (requires Docker)
mvn verify -pl examples/testing -P integration-tests
# Run specific test class
mvn test -pl examples/testing -Dtest=JUnit5FeaturesTest
# Run tagged tests
mvn test -pl examples/testing -Dgroups=slow
# With coverage report (target/site/jacoco/index.html)
mvn verify -pl examples/testingThe standard Java testing framework. Key features demonstrated:
@DisplayName— readable test names in reports@Nested— group related tests in inner classes@ParameterizedTest— run same test with different inputs (@ValueSource,@CsvSource,@MethodSource)@Tag— filter tests by category (mvn test -Dgroups=slow)@EnabledOnOs— conditional executionassertAll— grouped assertions that report all failuresassertTimeout— fail if operation exceeds duration
Fluent assertion library (preferred over JUnit's built-in assertions):
assertThat(user.name()).isEqualTo("Alice");
assertThat(list).hasSize(3).contains("a", "b");
assertThatThrownBy(() -> riskyCall())
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("failed");Mock dependencies to isolate the unit under test:
@Mock— create mock instances@InjectMocks— auto-inject mocks into constructor@Captor— capture arguments for inspectionwhen(...).thenReturn(...)— stub return valuesverify(...)— assert interactions occurred- BDD style —
given(...).willReturn(...)/then(...).should() spy(...)— partial mock wrapping a real object
Spin up real infrastructure (databases, message brokers, etc.) in Docker:
@Testcontainers
class MyIT {
@Container
static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16-alpine");
@Test
void test() {
String url = pg.getJdbcUrl(); // real JDBC URL
}
}Available modules: postgresql, mysql, mongodb, kafka, redis, elasticsearch, localstack, and many more.
Fluent HTTP API testing:
given()
.contentType(ContentType.JSON)
.body("""{"name":"Alice"}""")
.when()
.post("/api/users")
.then()
.statusCode(201)
.body("name", equalTo("Alice"));Enforce architecture rules as tests — no special tooling needed:
@ArchTest
static final ArchRule rule = noClasses()
.that().resideInAPackage("..repository..")
.should().dependOnClassesThat().resideInAPackage("..service..");Selenium is not included as a dependency (it requires a browser), but here's the pattern:
// pom.xml: org.seleniumhq.selenium:selenium-java:4.18.1
// io.github.bonigarcia:webdrivermanager:5.7.0
@Test
void loginFlow() {
WebDriverManager.chromedriver().setup();
var driver = new ChromeDriver();
try {
driver.get("http://localhost:8080/login");
driver.findElement(By.id("username")).sendKeys("admin");
driver.findElement(By.id("password")).sendKeys("secret");
driver.findElement(By.id("submit")).click();
var welcome = driver.findElement(By.id("welcome"));
assertThat(welcome.getText()).contains("Welcome");
} finally {
driver.quit();
}
}For modern projects, consider Playwright (com.microsoft.playwright:playwright:1.42.0) as a faster alternative with auto-wait and better API.
| Pattern | Plugin | When |
|---|---|---|
*Test.java |
Surefire | mvn test |
*IT.java |
Failsafe | mvn verify -P integration-tests |
- One assertion concept per test — test one behavior, use descriptive names
- Arrange-Act-Assert (or Given-When-Then) — consistent structure
- Don't mock what you don't own — wrap third-party APIs, mock the wrapper
- Use TestContainers over H2 — test against real databases
- Keep unit tests fast — mock I/O, no network, no disk
- Integration tests in CI — run with Docker in pipeline, skip locally if needed
- Coverage as a guide, not a goal — aim for meaningful coverage, not 100%
- Test behavior, not implementation — tests should survive refactoring
- Main README — Project overview and quick start
- Development Workflow — CI/CD, quality gates
- Documentation Standards — Test documentation conventions
- Security Scanning — Quality tool configuration
- Tutorial — New developer walkthrough