Skip to content

Commit b6dec87

Browse files
committed
update content
1 parent e2836e5 commit b6dec87

8 files changed

Lines changed: 136 additions & 43 deletions

File tree

labs/lab-7/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
## Learning Objectives
44

5-
- Configure JUnit 5 parallel test execution via `junit-platform.properties`
6-
- Understand the difference between JUnit 5 thread-level parallelism and Maven Surefire `forkCount` process-level forking
5+
- Configure JUnit 6 parallel test execution via `junit-platform.properties`
6+
- Understand the difference between JUnit 6 thread-level parallelism and Maven Surefire `forkCount` process-level forking
77
- Identify and fix test isolation issues caused by shared database state
88
- Apply `@Transactional` and UUID-based unique data as complementary isolation strategies
99

1010
## Key Concepts
1111

12-
### JUnit 5 Parallel Execution
12+
### JUnit 6 Parallel Execution
1313

14-
JUnit 5 supports running tests in parallel at both the class level and the method level. Configuration lives in `src/test/resources/junit-platform.properties`.
14+
JUnit 6 supports running tests in parallel at both the class level and the method level. Configuration lives in `src/test/resources/junit-platform.properties`.
1515

1616
There are two independent axes of parallelism:
1717

@@ -71,12 +71,12 @@ src/test/resources/
7171

7272
### Exercise 1: Configure and Observe Parallel Test Execution
7373

74-
Understand JUnit 5 parallel execution configuration and observe its effect on thread allocation.
74+
Understand JUnit 6 parallel execution configuration and observe its effect on thread allocation.
7575

7676
**Tasks:**
7777
1. Open `src/test/resources/junit-platform.properties` and review the current settings
7878
2. Open `Exercise1ParallelExecutionTest.java` — the test methods print the current thread name
79-
3. Run `mvn test` and observe which threads each test class runs on in the console output
79+
3. Run `mvn test` or within IntelliJ and observe which threads each test class runs on in the console output
8080
4. Try different parallelism strategies by modifying `junit-platform.properties`:
8181
- Classes concurrent, methods `same_thread` (current default)
8282
- Both classes and methods `concurrent`

labs/lab-7/src/test/java/pragmatech/digital/workshops/lab7/exercises/Exercise1ParallelExecutionTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* <ol>
1010
* <li>Check the junit-platform.properties file in src/test/resources</li>
1111
* <li>Run all tests and observe parallel execution in the log output</li>
12+
* <li>Consult <a href="https://docs.junit.org/6.0.3/writing-tests/parallel-execution.html">JUnit Docs</a></li>
1213
* <li>Experiment with different parallelism strategies:
1314
* <ul>
1415
* <li>a) Classes concurrent, methods same_thread (default - current setting)</li>

labs/lab-7/src/test/java/pragmatech/digital/workshops/lab7/exercises/Exercise2TestIsolationTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
*
2020
* <p>Tasks:
2121
* <ol>
22-
* <li>Identify tests that fail when run in parallel (shared database state)</li>
2322
* <li>Apply isolation strategies:
2423
* <ul>
2524
* <li>a) Use @Transactional for automatic rollback after each test</li>
6.56 KB
Loading

slides/assets/tsbad-cover.png

2.87 MB
Loading

slides/lab-5.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Philip Riecks - [PragmaTech GmbH](https://pragmatech.digital/) - [@rieckpil](htt
4646
- 9:00 - 10:30: **Integration Testing Continued - Verify the Entire Application**
4747
- 10:30 - 11:00: **Coffee Break & Time for Exercises**
4848
- 11:00 - 12:30: **Understanding Spring TestContext Context Caching for fast Tests**
49-
- 12:30 - 13:30: **Lunch**
49+
- 12:30 - 13:30: **Lunch & Time for Exercises**
5050
- 13:30 - 15:00: **Strategies for Fast and Reproducible Spring Boot Test Suites**
5151
- 15:00 - 15:30: **Coffee Break & Time for Exercises**
5252
- 15:30 - 16:30: **General Spring Boot Testing Tips & Tricks and Q&A**
@@ -461,4 +461,5 @@ pragmatech.digital.workshops.lab5.experiment.customizer.SharedInfrastructureCust
461461
- Navigate to the `labs/lab-5` folder and complete the tasks in the `README`
462462
- **Exercise 1**: Implement an integration test with `MockMvc` (MOCK environment, `@Transactional`)
463463
- **Exercise 2**: Implement the same scenario with `WebTestClient` (RANDOM_PORT, manual cleanup)
464+
- **Exercise 3** (Optional): Implement the exercise from lab 4 to learn how to use WireMock
464465
- Time boxed: until the end of the coffee break

slides/lab-7.md

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Philip Riecks - [PragmaTech GmbH](https://pragmatech.digital/) - [@rieckpil](htt
4545

4646
**Goal**: Reduce build time by running tests concurrently
4747

48-
Two independent mechanisms they work at different levels:
48+
Two independent mechanisms - they work at different levels:
4949

5050
| Mechanism | Level | Isolation |
5151
|---|---|---|
@@ -70,9 +70,9 @@ Splits tests across multiple **separate JVM processes**:
7070
</plugin>
7171
```
7272

73-
- `forkCount=1` default: one JVM for all tests
74-
- `forkCount=2` two JVMs running in parallel
75-
- `forkCount=1C` one JVM per available CPU core (dynamic)
73+
- `forkCount=1` - default: one JVM for all tests
74+
- `forkCount=2` - two JVMs running in parallel
75+
- `forkCount=1C` - one JVM per available CPU core (dynamic)
7676

7777
> **Maven Failsafe** works the same way for `*IT.java` integration tests.
7878
@@ -266,9 +266,7 @@ class BookIT {
266266

267267
---
268268

269-
## Optimize Containers Time
270-
271-
### Correct Usage of Testcontainers
269+
# Correct Usage of Testcontainers
272270

273271
---
274272

@@ -402,9 +400,7 @@ testcontainers.reuse.enable=true
402400

403401
## Tip 1: Hide Test Output, Show on Failure
404402

405-
By default, all test output floods the console.
406-
407-
Redirect it to files:
403+
By default, all test output floods the console. We can redirect it to files:
408404

409405
```xml
410406
<plugin>
@@ -417,6 +413,14 @@ Redirect it to files:
417413
</plugin>
418414
```
419415

416+
```yaml
417+
# as a last step on GitHub actions
418+
- name: Log test output on failure
419+
if: failure() || cancelled()
420+
run: find . -type f -path "*test-reports/*-output.txt" -exec tail -n +1 {} +
421+
422+
```
423+
420424
---
421425

422426
## Tip 2: Rerun Flaky Tests Automatically
@@ -533,6 +537,90 @@ Separate CI-specific settings from the default developer experience:
533537

534538
---
535539

540+
## GHA Workflow Design: Start Pragmatic
541+
542+
One file, one workflow - perfectly fine for most projects:
543+
544+
```yaml
545+
# .github/workflows/build.yml
546+
name: Build
547+
on: [push, pull_request]
548+
549+
jobs:
550+
build:
551+
runs-on: ubuntu-latest
552+
timeout-minutes: 20
553+
steps:
554+
- uses: actions/checkout@v4
555+
- uses: actions/setup-java@v4
556+
with: { java-version: '21', distribution: 'temurin', cache: maven }
557+
- run: ./mvnw verify
558+
```
559+
560+
**Start here.** Measure total build time. Only split when you have a concrete reason (too slow, unrelated failure domains, different schedules).
561+
562+
---
563+
564+
## GHA Workflow Design: When to Split
565+
566+
Once the single workflow grows unwieldy, extract by **concern and cadence**:
567+
568+
```text
569+
.github/workflows/
570+
build.yml ← on: push/PR — compile + unit + integration tests
571+
coding-standards.yml ← on: push/PR — Checkstyle, SpotBugs, ArchUnit
572+
nightly.yml ← on: schedule — slow E2E, mutation tests, full matrix
573+
deploy.yml ← on: push to main — build image + deploy to staging
574+
```
575+
576+
Separate workflows run **in parallel** automatically - no extra config needed.
577+
578+
---
579+
580+
## Off-topic: pre-commit — Catch Issues Before You Push
581+
582+
[pre-commit](https://pre-commit.com) runs checks automatically on `git commit`, before code ever reaches CI:
583+
584+
```yaml
585+
repos:
586+
- repo: https://github.com/pre-commit/pre-commit-hooks
587+
hooks:
588+
- id: check-yaml
589+
- id: end-of-file-fixer
590+
- id: trailing-whitespace
591+
- repo: https://github.com/ejba/pre-commit-maven
592+
hooks:
593+
- id: maven
594+
args: ['-f pom.xml spotless:apply']
595+
```
596+
597+
Style violations and formatting errors fail **locally in milliseconds** - not 5 minutes into a CI run. Less noise in PRs, faster feedback loop.
598+
599+
600+
---
601+
602+
## Best Practice 0: Never Go Stale
603+
604+
**Deploy or run at least once a week** - even if nobody pushed code.
605+
606+
```yaml
607+
# .github/workflows/nightly.yml
608+
name: Nightly Verification
609+
on:
610+
schedule:
611+
- cron: '0 3 * * 1' # Every Monday at 03:00 UTC
612+
workflow_dispatch: # Allow manual trigger
613+
614+
jobs:
615+
verify:
616+
runs-on: ubuntu-latest
617+
```
618+
619+
Stale pipelines break silently: secrets expire, base images get security patches, dependency resolution drifts. A weekly run catches this **before** it blocks a real release.
620+
621+
---
622+
623+
536624
## Best Practice 1: Always Set a Job Timeout
537625

538626
Hanging Testcontainers or infinite loops block CI slots for hours without a timeout:

slides/lab-8.md

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class OutputCaptureTest {
8282

8383
---
8484

85-
# Mutation Testing with PIT
85+
## Mutation Testing with PIT
8686

8787
**The problem:** 100% line coverage ≠ good tests. Weak assertions can still pass.
8888

@@ -99,15 +99,13 @@ void shouldReturnFee() {
9999
```bash
100100
cd labs/lab-8
101101
./mvnw pitest:mutationCoverage
102-
# Opens target/pit-reports/index.html
102+
# Open target/pit-reports/index.html
103103
```
104104

105-
> **A mutation is "killed"** when at least one test fails because of the mutation.
106-
> **A surviving mutant** exposes a gap in your test suite.
107105

108106
---
109107

110-
## PIT in Action `LateReturnFeeCalculator`
108+
## PIT in Action - `LateReturnFeeCalculator`
111109

112110
```java
113111
// Production code with multiple fee tiers
@@ -125,7 +123,8 @@ public BigDecimal calculateFee(Book book, LocalDate borrowedDate) {
125123

126124
**PIT mutates conditionals** (`<= 7``< 7`, `<= 14``< 14`) — boundary tests are essential.
127125

128-
**Pro tip:** Use `Clock` injection instead of `LocalDate.now()` for deterministic tests.
126+
> **A mutation is "killed"** when at least one test fails because of the mutation.
127+
> **A surviving mutant** exposes a gap in your test suite.
129128
130129
---
131130

@@ -194,21 +193,14 @@ class RecordApplicationEventsTest {
194193

195194
## `ApplicationContextRunner`
196195

197-
Test **auto-configuration and conditional beans** without starting a full Spring Boot context.
196+
Test **auto-configuration and conditional beans** without starting a full context.
198197

199198
```java
200199
class ApplicationContextRunnerTest {
201200

202201
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
203202
.withUserConfiguration(ConditionalBookImportConfig.class); // ← Minimal slice
204203

205-
@Test
206-
void shouldNotHaveBookImportBeanWhenPropertyIsAbsent() {
207-
contextRunner.run(context ->
208-
assertThat(context).doesNotHaveBean("bookImportEnabled")
209-
);
210-
}
211-
212204
@Test
213205
void shouldHaveBookImportBeanWhenPropertyIsEnabled() {
214206
contextRunner
@@ -220,7 +212,7 @@ class ApplicationContextRunnerTest {
220212
}
221213
```
222214

223-
**Runs in milliseconds** ideal for testing `@ConditionalOnProperty`, `@ConditionalOnClass`, `@ConditionalOnMissingBean`.
215+
**Runs in milliseconds** - ideal for testing `@ConditionalOnProperty`, `@ConditionalOnClass`, `@ConditionalOnMissingBean`.
224216

225217
---
226218

@@ -253,7 +245,7 @@ class ApplicationContextRunnerTest {
253245

254246
---
255247

256-
## ArchUnit Code Examples
248+
## ArchUnit - Code Examples
257249

258250
```java
259251
@AnalyzeClasses(
@@ -535,7 +527,7 @@ log.info("Book created");
535527
**Good observability checklist:**
536528
- Structured JSON logs (Logback + `logstash-logback-encoder`) shipped to a central store
537529
- Dashboards scoped to **error rate**, **p99 latency**, and **business KPIs** - not CPU graphs
538-
- Runbooks linked directly from alert notifications
530+
- [Runbooks](https://github.com/stratospheric-dev/stratospheric/tree/main/docs/runbooks) linked directly from alert notifications
539531

540532
---
541533

@@ -556,13 +548,6 @@ commit → CI (tests pass) → build image → push to registry
556548

557549
---
558550

559-
**Rollback triggers to Monitor**
560-
- Health check endpoint (`/actuator/health`) returns non-200 for > 60 s after deploy
561-
- Error rate spikes above baseline within the first 5 minutes
562-
- P99 latency exceeds SLO threshold post-deploy
563-
564-
---
565-
566551
## Blue/Green & A/B Deployments
567552

568553
**Blue/green** eliminates downtime and provides instant rollback: run two identical environments, flip the load balancer, keep the old environment warm.
@@ -600,7 +585,9 @@ Requires feature-flag or traffic-splitting infrastructure (Nginx, Istio, AWS ALB
600585
## Feature Flags: Decouple Deployment from Release
601586

602587
**Deployment** = shipping code to production.
603-
**Release** = making a feature visible to users. Feature flags let you do both independently.
588+
**Release** = making a feature visible to users.
589+
590+
Feature flags let you do both independently.
604591

605592
```java
606593
if (featureFlags.isEnabled("new-book-search", userId)) {
@@ -728,6 +715,23 @@ The goal is not zero failures - it is **fast, automatic recovery** when failures
728715

729716
---
730717

718+
## Don't Leave Empty-Handed
719+
720+
![bg h:720 w:450 right:33%](assets/tsbad-cover.png)
721+
722+
723+
- Get the complementary **Testing Spring Boot Applications Demystified** for free
724+
725+
- 120+ Pages with practical hands-on advice to ship code with confidence
726+
727+
- Get the eBook by joining our [newsletter](https://rieckpil.de/free-spring-boot-testing-book/) and receive further Spring Boot testing-related tips & tricks
728+
729+
![center h:200 w:200](assets/newsletter-signup-qr.png)
730+
731+
---
732+
733+
<!-- paginate: false -->
734+
731735
## Joyful Testing!
732736

733737
Workshop materials are on [GitHub](https://github.com/PragmaTech-GmbH/digdir-workshop/).

0 commit comments

Comments
 (0)