Use multiple test layers. A unit test of a service does not prove that the package exports, provider order, migrations, database driver, and Next.js runtime work together.
Unit tests#
Test domain services with fake repositories and adapters where the package architecture exposes ports. Verify success paths, expected errors, and edge cases. Avoid coupling unit tests to a concrete database unless persistence is the behavior under test.
Container isolation#
The kernel container and event bus are shared singletons in the global runtime. Tests that mutate them should reset or clean them between cases and should not run concurrently against the same global state unless isolation is guaranteed.
Database integration tests#
Run tests against the database engine used in production. Include:
- migrations from an empty database;
- migrations from the previous release schema;
- model create/read/update/delete behavior;
- relations and eager loading;
- transactions and rollback;
- uniqueness and foreign-key constraints;
- soft deletes and scopes where used.
SQLite tests alone may not catch PostgreSQL-specific SQL, collation, JSON, transaction, or type behavior.
Provider tests#
Verify that providers register the expected tokens, boot in the intended order, and clean up resources on shutdown. Test failed boot and retry behavior.
Next.js integration tests#
Exercise Server Actions and Route Handlers in the actual Next.js version. Confirm that redirects and not-found control flow are preserved, that no server-only imports enter client bundles, and that build-time rendering does not depend on bootstrap side effects.
Documentation examples#
Compile runnable examples against the exact locked package versions. Examples that depend on deployment secrets, mail delivery, or external storage should be marked as integration tests and run in a controlled environment.