You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+19-29Lines changed: 19 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,11 +8,13 @@ Proudly presented by [PragmaTech GmbH](https://pragmatech.digital/).
8
8
9
9
## Workshop Overview
10
10
11
-
This workshop is designed to demystify testing in Spring Boot applications through hands-on exercises, covering everything from basic unit testing to advanced integration testing techniques, test optimization, and CI/CD best practices. The workshop is divided into eight lab sessions across two days, each focusing on different aspects of testing Spring Boot applications.
11
+
This workshop is designed to demystify testing in Spring Boot applications through hands-on exercises, covering everything from basic unit testing to advanced integration testing techniques, test optimization, and CI/CD best practices.
12
+
13
+
The workshop is divided into eight lab sessions across two days, each focusing on different aspects of testing Spring Boot applications.
12
14
13
15
### Day One: Testing Spring Boot Applications Demystified
14
16
15
-
Goal: Getting started with testing Spring Boot applications and learning how to write tests for Spring Boot applications.
17
+
**Goal**: Getting started with testing Spring Boot applications and learning how to write tests for Spring Boot applications.
16
18
17
19
| Time | Session |
18
20
|------|---------|
@@ -26,28 +28,21 @@ Goal: Getting started with testing Spring Boot applications and learning how to
26
28
27
29
### Day Two: The Need for Speed: Optimizing Spring Boot Test Suites
28
30
29
-
Goal: Showcase and explain strategies to improve build times and speed up tests to gather fast feedback.
MockMvc dispatches requests through the `DispatcherServlet` in the **same thread** as the test. When `@Transactional` is present on the test class, every test method runs inside a transaction that rolls back automatically at the end — no cleanup code needed.
26
+
27
+
### WebTestClient + `@AfterEach` Pattern
28
+
29
+
WebTestClient sends real HTTP requests to the embedded server. Those requests are handled in a **different server thread**, so the server commits its transaction independently. `@Transactional` on the test class has no effect on what the server commits. Manual cleanup (e.g., `bookRepository.deleteAll()`) in `@AfterEach` is required.
18
30
19
31
## Exercises
20
32
21
-
### Exercise 1: Testing HTTP Clients with WireMock
33
+
### Exercise 1: Integration Testing with MockMvc
22
34
23
-
In this exercise, you'll create unit tests for the `OpenLibraryApiClient` using WireMock to simulate HTTP interactions.
35
+
Write a full integration test using `@SpringBootTest` with the default MOCK web environment.
24
36
25
37
**Tasks:**
26
-
1. Open `Exercise1WireMockTest.java` in the `exercises` package
27
-
2. Implement test methods to verify client behavior for:
28
-
- Successful API responses (200 status)
29
-
- Server error responses (500 status)
30
-
3. Optional: Modify the client to handle 404 responses gracefully by returning null instead of throwing an exception
38
+
1. Open `Exercise1MockMvcIntegrationTest.java` in the `exercises` package
- POST an invalid body (missing fields or wrong ISBN format) and assert 400 Bad Request
31
49
32
50
**Tips:**
33
-
- Use WireMock's JUnit 5 extension (`@RegisterExtension`) or bootstrap it manually
34
-
- Configure `WebClient` to point to the WireMock server in your test
35
-
- Use WireMock's `stubFor()` method to define response behavior
36
-
- Sample response JSON files are available in the `__files` directory (WireMock's default source folder for stubbing)
37
-
- Check the solution in `Solution1WireMockTest.java`
51
+
- ISBN must be in the format `"978-XXXXXXXXXX"` (dashes required by `BookCreationRequest`)
52
+
-`GET /api/books` is public; `GET /api/books/{id}` and `POST /api/books` require `USER` role
53
+
- Use `MockMvcRequestBuilders.post(...)` and `MockMvcResultMatchers.jsonPath(...)`
54
+
- Inject `WireMockServer` as a bean or use the provided `OpenLibraryApiStub` wrapper
55
+
56
+
**Observe:** Because `@Transactional` is on the class and MockMvc runs in the same thread, all database changes roll back automatically after each test — no `@AfterEach` cleanup needed.
- GET `/api/books/1` without credentials and assert 401 Unauthorized
79
+
80
+
**Tips:**
81
+
- Use `.headers(h -> h.setBasicAuth("admin", "admin"))` for `ADMIN` role endpoints
82
+
-`GET /api/books/{id}` requires `USER` role; without credentials the server returns 401
83
+
84
+
**Observe:**`@Transactional` on the test class does NOT prevent the server from committing changes. The `@AfterEach` cleanup is essential to keep tests independent.
Copy file name to clipboardExpand all lines: labs/lab-6/README.md
+52-30Lines changed: 52 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,12 +5,12 @@
5
5
- Understand how Spring's TestContext caching mechanism works
6
6
- Identify common configuration mistakes that break context caching
7
7
- Measure the performance impact of `@DirtiesContext` and `@MockitoBean`
8
-
- Apply the SharedIntegrationTestBase pattern to maximize context reuse
8
+
- Apply the `SharedIntegrationTestBase` pattern to maximize context reuse
9
9
- Reduce integration test suite execution time
10
10
11
11
## How Context Caching Works
12
12
13
-
Spring's TestContext framework caches application contexts between test classes to avoid the expensive cost of starting a new context for every test. A single Spring Boot context startup typically takes 5-10 seconds, so a test suite with 20 integration test classes could take 100-200 seconds just on context initialization if caching is broken.
13
+
Spring's TestContext framework caches application contexts between test classes to avoid the expensive cost of starting a new context for every test. A single Spring Boot context startup typically takes 5–10 seconds, so a test suite with 20 integration test classes could take 100–200 seconds just on context initialization if caching is broken.
14
14
15
15
The context cache key is composed of:
16
16
@@ -34,13 +34,13 @@ If two test classes have the **exact same** combination of these attributes, the
34
34
|`@Transactional`| No | Only affects transaction behavior, not the context key |
35
35
| Different test method names | No | Methods are not part of the cache key |
36
36
37
-
## The @DirtiesContext Problem
37
+
## The `@DirtiesContext` Problem
38
38
39
39
`@DirtiesContext` is the most expensive annotation in terms of test performance. It marks the context as "dirty" and forces Spring to destroy it and create a new one:
40
40
41
41
```java
42
-
// AVOID: Destroys and recreates context after EVERY test method
Create a `SharedIntegrationTestBase` and refactor all integration tests to extend it, ensuring only a single application context is created across the entire test suite.
116
+
Apply the `SharedIntegrationTestBase` pattern to eliminate duplicate context creation across the suite.
117
+
118
+
**Tasks:**
119
+
1. Open `Exercise2SharedBaseClassTest.java` — make the class extend `SharedIntegrationTestBase` (already provided in the `config` package)
120
+
2. Add an `@Autowired BookRepository` field and assert it is not null in the test
121
+
3. Refactor the `ContextCacheKiller*IT` tests to extend `SharedIntegrationTestBase`:
122
+
- Remove duplicate `@SpringBootTest`, `@Import`, `@ContextConfiguration` from each test
123
+
- Remove `@DirtiesContext` — it defeats caching entirely
124
+
- Remove `@MockitoBean` annotations — use WireMock stubs from the shared context instead
125
+
- Remove `@TestPropertySource` with unique values — move shared properties to `application-test.yml`
126
+
- Remove `@ActiveProfiles` differences — keep profiles consistent across the suite
127
+
4. Run all integration tests and verify only ONE `"Initializing Spring"` log line appears
128
+
5. Compare build time before and after the optimization
0 commit comments