How to test modules in Base

Test a module in isolation, letting dependency injection resolve the service in a fresh scope.

Testing
AhraJuly 22, 2026

How to test modules in Base

A module in Base is a self-contained unit: it brings its own services, its own configuration, its own piece of the worker. Because it is self-contained, you can test it on its own, without standing up an entire worker around it. This post is about that altitude of testing, the module in isolation, fast and focused. It assumes you have read the testing overview and the modules post.

Why test a module in isolation

The point of a module is composition: you drop it into a worker and it adds a capability. The point of testing it in isolation is confidence in that capability before it ever meets a real worker. If a module's services do their job correctly on their own, then most of what can go wrong when you compose it is wiring, which the integration tests catch. So module tests carry the bulk of the correctness load, and they carry it cheaply, because they do not pay the cost of a live server.

Testing the module where the module lives also keeps the test honest. The test exercises the module's real services and real logic, not a mock of them. When it passes, the thing that passed is the thing that ships.

Resolve what you need, test it directly

Base is built on dependency injection, and that is exactly what makes module testing clean. Rather than constructing a service and hand-feeding it every dependency, you let the container resolve it, the same way the running worker would. You set up the dependencies the module's code expects, ask the container for the service under test, and call its methods. Because resolution can happen in an isolated scope, one test's setup does not leak into the next, and you get a fresh, predictable instance for each case.

This is the quiet payoff of dependency injection showing up in your tests. The same mechanism that decouples your code at runtime decouples it under test: you swap a real dependency for a test one by registering the test one, and the service under test never knows the difference, because it only ever asked for an interface.

Keep the surface small

A good module test touches one thing. It resolves one service, exercises one behavior, and asserts on one outcome. When a module test starts needing half the worker stood up to run, that is the signal that you have drifted from a module test into an integration test, and that the behavior you are checking actually lives at the integration altitude. Let it move there. Keep the module tests for the logic that genuinely stands alone.

The mental model to keep

Testing a module in Base means testing it in isolation, without a live worker: lean on dependency injection to resolve the service under test in a fresh scope, swap real dependencies for test ones by registering them, and assert on real behavior. Keep each test small and focused on one thing, and when a test needs the whole worker, recognize that as an integration test and let it move to that altitude. Module tests are where most of your correctness load belongs, because they are fast, honest, and isolated.