Lab kiểm thử và chất lượng
Các lab xây dần một Transfer Service. Mỗi bài chọn boundary nhỏ nhất đủ chứng minh rủi ro; luôn ghi expected result và evidence thay vì chỉ ghi test đã chạy.
Lab 01 · Domain test bằng JUnit
Yêu cầu: viết Account/TransferPolicy và kiểm tra amount dương, insufficient balance, currency mismatch, overflow và state không đổi khi fail.
@Test
void shouldKeepBalanceWhenDebitFails() {
var account = new Account(new BigDecimal("10.00"));
assertThrows(InsufficientBalanceException.class,
() -> account.debit(new BigDecimal("20.00")));
assertEquals(new BigDecimal("10.00"), account.balance());
}
Mở rộng: thêm parameterized edge cases, inject Clock cho daily limit và chạy mutation test cho package domain.
Lab 02 · Controller boundary
Dùng @WebMvcTest và MockMvc kiểm tra JSON mapping, validation, status code, error body, content type và authorization rule. Mock application service nhưng chỉ verify command quan trọng, không verify mọi call nội bộ.
Khung test
@WebMvcTest(TransferController.class)
class TransferControllerTest {
@Autowired MockMvc mvc;
@MockBean TransferService service;
@Test void shouldRejectInvalidAmount() throws Exception {
mvc.perform(post("/transfers")
.contentType(MediaType.APPLICATION_JSON)
.content("""{"from":"A","to":"B","amount":0}"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value("VALIDATION_ERROR"));
verifyNoInteractions(service);
}
}Lab 03 · PostgreSQL Testcontainers
Test mapping, unique idempotency key, query, optimistic locking và commit visibility với PostgreSQL container. Thêm component test cho flow request → service → committed database state. Không dùng H2 để thay thế các semantics của production database.
- Mở hai transaction cùng đọc một
Accountversion. - Transaction đầu commit; transaction sau phải optimistic-lock failure.
- Hai request cùng idempotency key chỉ tạo một transfer.
- Reset data giữa tests mà không restart container nếu có thể.
Container khởi đầu
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:17-alpine");Lab 04 · Concurrency deterministic
Tạo phiên bản transfer cố ý race rồi sửa. Dùng CountDownLatch/Barrier để 20 workers bắt đầu cùng lúc; mọi Future có timeout.
- Assert tổng tiền không đổi, không balance âm và số thành công đúng.
- Ghi lại interleaving hoặc lock evidence để failure tái hiện được.
- Không dùng
sleepđể “hy vọng” collision; không chỉ in kết quả rồi cho test pass.
Lab 05 · Duplicate, retry và messaging
Tách handler business khỏi broker adapter. Unit test handler; integration test broker container.
- Gửi cùng event ID hai lần, assert một ledger entry.
- Dependency fail hai lần rồi thành công, assert ba attempts và backoff policy.
- Permanent failure đi DLQ sau giới hạn.
- Restart consumer giữa processing để kiểm tra redelivery.
- Ở integration test, assert persistence side effect thay vì dùng verify mock thay thế broker/database.
Lab 06 · CI quality gate và flaky drill
Tạo pipeline chạy unit → integration → report. Thêm JaCoCo, static/dependency scan và PIT cho domain package; lưu XML/HTML report khi fail.
- Tạo flaky test phụ thuộc timezone, order hoặc random.
- Chạy lặp để thu seed, order, duration và environment evidence.
- Sửa bằng
Clock, seed hoặc isolation, không chỉ tăng timeout. - Chứng minh test ổn định khi chạy parallel và đổi timezone.
- Ghi threshold có lý do, không đặt 100% máy móc.
Lab 07 · Performance regression và saturation
Xây workload model cho Transfer API với warm-up, data volume và target throughput. Đo p50/p95/p99, error, CPU/GC và DB pool wait; chạy load, spike và soak ngắn.
- Inject N+1 query hoặc pool nhỏ để tạo regression.
- So baseline/candidate cùng environment và ghi uncertainty.
- Đặt automated regression budget có tolerance, không fail theo một sample.
- Lưu report, flame graph hoặc query evidence và xác nhận bottleneck sau fix.
Lab 08 · Security và supply-chain gate
Threat-model trust boundaries; thêm authorization/data-isolation tests, SAST, SCA, secret và container scan. Mô phỏng untrusted PR/cached artifact risk mà không dùng secrets thật.
- Tạo một vulnerable dependency/finding có kiểm soát và triage reachability.
- Policy fail hoặc allowlist phải có severity, owner, expiry và evidence.
- Dùng synthetic/masked test data; chứng minh report/log không chứa token hoặc PII.
- Tách PR runner permissions khỏi publish/deploy identity và verify immutable artifact.
Rubric
| Mức | Tiêu chí |
|---|---|
| 0 | Test không chạy ổn định hoặc chỉ happy path. |
| 1 | Behavior chính đúng, assertion rõ. |
| 2 | Boundary phù hợp, có failure/edge và resource isolation. |
| 3 · Senior | Chứng minh transaction/concurrency/performance/security semantics, CI evidence và giải thích trade-off. |