Transactions per test, and the three things that break

Wrapping each test in a transaction and rolling back is much faster than migrating between tests, and three things do not survive it.

use RefreshDatabase;   // transaction per test, rolled back

// what breaks:
//   1. code under test that commits, or uses DDL —
//      MySQL commits implicitly on CREATE TABLE
//   2. a second connection: a browser test, a queue
//      worker, anything not in this transaction
//   3. assertions about what a REAL commit triggers
//      — after-commit callbacks never fire

The second connection case is the one that produces the most confusing failures: a browser driver hitting the application over HTTP opens its own connection and cannot see the uncommitted fixtures, so the page renders empty. That is what DatabaseTruncation or a migrated database is for, at a real speed cost. After-commit hooks silently not running is the subtle one, because the test passes and the behaviour is untested.