Loading published curriculum…

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

  1. 01Write a test proving a successful service call commits and a failed call rolls back.
  2. 02Add a coordinated optimistic-locking race with bounded latches.
  3. 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.

  1. 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.
  2. 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.
  3. 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?

Choose one answer

BUILD IT YOURSELF

Write a test proving a successful service call commits and a failed call rolls back.

  1. 1.Write a test proving a successful service call commits and a failed call rolls back.
  2. 2.Add a coordinated optimistic-locking race with bounded latches.
  3. 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.

Go deeper · official references