18 · Testing the complete backend / LESSON 92 OF 102
Testing commits, rollback, and concurrent updates
Transaction tests need to observe the actual commit boundary, not just in-memory entity state.
Before this lesson: PostgreSQL integration tests with Testcontainers
Understand the topic
A test wrapped in a transaction may roll back automatically and hide failures that occur at commit. Use explicit transaction templates or separate service calls when verifying committed state. Reload through a fresh persistence context to avoid first-level cache illusions.
Concurrency tests coordinate two transactions with barriers or latches so both read the same version before either commits. Avoid hoping arbitrary sleep durations produce a race. Bound waits so failures do not hang the entire suite.
EXAMPLE · JAVA
TransactionTemplate test fragment
var tx = new org.springframework.transaction.support.TransactionTemplate(transactionManager);
Long id = tx.execute(status -> repository.save(new Task("Locking lab")).getId());
entityManager.clear();
Boolean exists = tx.execute(status -> repository.existsById(id));
org.junit.jupiter.api.Assertions.assertEquals(Boolean.TRUE, exists);What to expect
Adapt the Task constructor/accessor to your entity. Execute outside an enclosing rollback-only test transaction so separate callbacks can commit and observe the row.
YOUR TURN
Put it into practice
- 01Write a test proving a successful service call commits and a failed call rolls back.
- 02Add a coordinated optimistic-locking race with bounded latches.
- 03Verify: The second stale update fails and a fresh transaction sees only the accepted state.
Pass a quiz or complete a lab to check today’s streak. Each lesson counts once per day, including when you review it later.
UNDER THE HOOD
Build the mental model
Transaction tests need to observe the actual commit boundary, not just in-memory entity state.
- 01A test wrapped in a transaction may roll back automatically and hide failures that occur at commit. Use explicit transaction templates or separate service calls when verifying committed state. Reload through a fresh persistence context to avoid first-level cache illusions.
- 02Concurrency tests coordinate two transactions with barriers or latches so both read the same version before either commits. Avoid hoping arbitrary sleep durations produce a race. Bound waits so failures do not hang the entire suite.
- 03Trace the example: Adapt the Task constructor/accessor to your entity. Execute outside an enclosing rollback-only test transaction so separate callbacks can commit and observe the row.
A mistake worth avoiding
A test passes because it reads the same cached entity instance after an operation.
DEBUGGING ROOM
Something broke. Where do you look?
A test passes because it reads the same cached entity instance after an operation.
Before revealing the diagnosis, write down your first investigation step.
CHECK YOUR UNDERSTANDING
Can a rollback-only test hide a failure that happens at commit?
BUILD IT YOURSELF
Write a test proving a successful service call commits and a failed call rolls back.
- 1.Write a test proving a successful service call commits and a failed call rolls back.
- 2.Add a coordinated optimistic-locking race with bounded latches.
- 3.Reproduce this failure, then repair it: A test passes because it reads the same cached entity instance after an operation.
Acceptance checklist
Lab completion is your self-report. Knowledge checks do not certify project quality.
RETRIEVE IT FROM MEMORY
Can a rollback-only test hide a failure that happens at commit?
Revisit this lesson in three days after a successful check or lab.
Ready for the next step?
The second stale update fails and a fresh transaction sees only the accepted state.
Reading is separate from passing a knowledge check or completing a lab.