Testing
TinyDI has no test-specific API, and does not need one. Registration and resolution are ordinary method calls, so testing looks like ordinary TypeScript: build a container, register a fake instead of the real implementation, resolve, assert.
A fresh container per test
A Singleton's cached instance lives on the Container it was built from, not in a global. Creating a new Container in each test's setup is enough to guarantee no state leaks between tests — there is no reset() step to remember.
import { Container, createToken } from 'tinydi-container';
interface IMailService {
send(to: string, body: string): Promise<void>;
}
const MailServiceToken = createToken<IMailService>('MailService');
class FakeMailService implements IMailService {
sent: { to: string; body: string }[] = [];
async send(to: string, body: string): Promise<void> {
this.sent.push({ to, body });
}
}
let container: Container;
let mail: FakeMailService;
beforeEach(() => {
container = new Container();
mail = new FakeMailService();
container.registerInstance(MailServiceToken, mail);
});
test('sends a welcome email on signup', async () => {
await onSignup(container, 'user@example.com');
expect(mail.sent).toHaveLength(1);
});
No mocking library needed
A fake is just a plain class or object implementing the same interface — TypeScript checks it structurally at compile time, exactly like the real implementation. There is nothing to import beyond tinydi-container itself.
Swapping a fake mid-test
remove() exists specifically for this: registering an already-registered token throws RegistrationError, on purpose, so call remove() first before re-registering it with a different fake.
// Later in the same test file, a different test needs the real
// implementation to fail so it can assert on the error path:
container.remove(MailServiceToken);
container.registerInstance(MailServiceToken, new FailingMailService());
What this does not cover
TinyDI does not provide spies, call-count assertions, or automatic mock generation. Pair it with your test runner's own assertion library (Vitest's expect, Jest's expect, or similar) for that — a fake registered via registerInstance is a plain object, so any assertion library works against it unchanged.