SUITES / TYPESCRIPT UNIT TESTING

Project Suites.

A unit testing framework for TypeScript backends working with inversion of control and dependency injection

Open source. Built for dependency injection.

user.service.spec.tsTypeScript
import { TestBed } from '@suites/unit';
import { UserService } from './user.service';
import { UserRepository } from './user.repository';
const { unit, unitRef } = await TestBed
.solitary(UserService)
.compile();
const repository = unitRef.get(UserRepository);
repository.getUserById.mockResolvedValue(user);
expect(await unit.getUserName(user.id))
.toBe(user.name);

FITS YOUR STACK

One testing pattern.
The tools you already use.

Dependency injection

  • NestJS
  • InversifyJS
  • InjectionJS

Test runners

  • Jest
  • Vitest

LESS WIRING. MORE TESTING.

Suites compared with manual mocking

Keep the test.
Let Suites handle the setup.

import { TestBed, type Mocked } from '@suites/unit';
describe('User Service', () => {
let userService: UserService; // The unit we are testing
let userApi: Mocked<UserApi>; // The dependencies we are mocking
let database: Mocked<Database>;
beforeAll(async () => {
// Create an isolated test env for the unit
const { unit, unitRef } = await TestBed.solitary(UserService).compile();
userService = unit;
// Retrieve the unit's dependency mocks - automatically generated
userApi = unitRef.get(UserApi);
database = unitRef.get(Database);
});
// ✅ Test test test
it('should generate a random user and save to the database', async () => {
userApi.getRandom.mockResolvedValue({id: 1, name: 'John'} as User);
await userService.generateRandomUser();
expect(database.saveUser).toHaveBeenCalledWith(userFixture);
});
});
// THE MANUAL WAY
// (Similar complexity exists in NestJS, InversifyJS, etc.)
import { UserService } from './user-service';
import { UserApi } from './user-api';
import { Database } from './database';
describe('User Service', () => {
let userService: UserService;
let userApi: any; // ❌ Lost type safety!
let database: any;
beforeAll(async () => {
// 🔧 Manually create mocks for each dependency
userApi = {
getRandom: jest.fn(),
// Missing methods? Who knows? No compile-time checks!
};
database = {
saveUser: jest.fn(),
// What other methods exist? ¯\_(ツ)_/¯
};
// Manual wiring - boilerplate for every test
userService = new UserService(userApi, database);
});
it('should generate a random user and save to the database', async () => {
userApi.getRandom.mockResolvedValue({id: 1, name: 'John'} as User);
await userService.generateRandomUser();
expect(database.saveUser).toHaveBeenCalledWith(userFixture);
});
}

A SMALL API. A CLEARER TEST.

Make room for the behavior
that matters.

Standardized Testing Across Teams

Stop relearning test patterns on every project. Suites provides a consistent, standardized approach that works identically across NestJS, InversifyJS, and InjectionJS, with Jest or Vitest as your test runner.

See framework support
// Same pattern works everywhere
// NestJS, InversifyJS, InjectionJS
TestBed.solitary(OrderService).compile();
// Or test with real dependencies
TestBed.sociable(OrderService)
.expose(PaymentProcessor)
.compile();

Tests That Reveal Intent, Not Boilerplate

Create the unit and its dependency mocks in one step. Keep setup short so the behavior you arrange, run, and assert is easy to find.

See quick start
// One line creates the entire test environment
const { unit, unitRef } = await TestBed
.solitary(UserService)
.compile();
// Just test the logic
const userApi = unitRef.get(UserApi);
userApi.getRandom.mockResolvedValue(user);
await unit.generateRandomUser();

Type-Safe, Automatic Mocking

No more debugging broken mocks. Suites automatically generates fully-typed mocks bound to implementation. Catch errors at compile time, not runtime. Refactor with confidence while mocks stay valid when dependencies change.

Learn about mocking
// No manual mocks needed!
const { unit, unitRef } = await TestBed
.solitary(UserService)
.compile();
// Fully-typed mocks, automatically generated
const userApi = unitRef.get(UserApi);
userApi.get.mockResolvedValue({ name: 'John' });

Built for AI-First Development

A consistent TestBed pattern gives coding assistants a small, repeatable example to follow. Spend your review on test behavior instead of generated mock classes and constructor wiring.

Suites and AI
// Manual: 40+ lines per test in AI context
// class MockUserApi { getRandom = jest.fn(); ... }
// class MockDatabase { saveUser = jest.fn(); ... }
// const userService = new UserService(mockUserApi, mockDb);
// Suites: Single canonical pattern
const { unit, unitRef } = await TestBed
.solitary(UserService)
.compile();
// AI agents learn the entire API from one example

START WITH ONE CLASS

Write your first Suites test.

Choose your framework and runner. Follow the same simple pattern.

Open the quickstart