#Unit Tests
Alepha uses Vitest as the test runner. The alepha init --test flag scaffolds a Vitest configuration and a sample test file. All tests run with globals: true, so you do not need to import test, expect, or describe.
#Setup
Scaffold a project with test support:
alepha init my-app --test
This installs Vitest and creates a vitest.config.ts with two test projects:
- Node tests -- all
*.spec.tsfiles (default environment) - Browser tests -- all
*.browser.spec.ts/*.browser.spec.tsxfiles (jsdom environment)
Run tests:
yarn test # All tests
yarn w alepha test # Single package
yarn w alepha vitest run init.spec # Filtered by pattern
#Lifecycle Management
Alepha.create() handles start and stop automatically in test environments. You do not need beforeAll/afterAll for lifecycle.
1test("should inject a service", async () => {2 const alepha = Alepha.create().with(MyService);3 const svc = alepha.inject(MyService);4 await alepha.start();5 6 const result = await svc.doSomething();7 expect(result).toBe("done");8});
#Testing Actions
Actions expose two call modes: .run() for in-process invocation and .fetch() for HTTP simulation.
1import { Alepha, t } from "alepha"; 2import { $action } from "alepha/server"; 3 4class UserController { 5 getUser = $action({ 6 path: "/users/:id", 7 schema: { params: t.object({ id: t.uuid() }) }, 8 handler: async ({ params }) => ({ id: params.id, name: "Alice" }), 9 });10}11 12test("should return user by id", async () => {13 const alepha = Alepha.create().with(UserController);14 const ctrl = alepha.inject(UserController);15 16 // Local call - no HTTP overhead17 const result = await ctrl.getUser.run({ params: { id: "abc-123" } });18 expect(result.name).toBe("Alice");19 20 // HTTP simulation - goes through the full request pipeline21 const response = await ctrl.getUser.fetch({ params: { id: "abc-123" } });22 expect(response.status).toBe(200);23});
#DI Substitution (No vi.mock)
Never use vi.mock() or vi.spyOn(). Alepha's dependency injection system replaces the need for traditional mocking. Use .with({ provide, use }) to swap implementations.
1const alepha = Alepha.create()2 .with({ provide: PaymentService, use: FakePaymentService });3 4const svc = alepha.inject(PaymentService);5// svc is now an instance of FakePaymentService
#Memory Providers
Alepha ships memory implementations for all I/O-bound services. These run in-process with no external dependencies and include test assertion helpers.
| Provider | Import | Replaces |
|---|---|---|
MemoryFileSystemProvider |
alepha/system |
File system |
MemoryShellProvider |
alepha/system |
Shell commands |
MemoryQueueProvider |
alepha/queue |
Job queues |
MemoryTopicProvider |
alepha/topic |
Pub/sub topics |
MemoryLockProvider |
alepha/lock |
Distributed locks |
MemoryCacheProvider |
alepha/cache |
Caching layer |
MemoryFileStorageProvider |
alepha/bucket |
File storage (S3, R2, etc.) |
MemoryEmailProvider |
alepha/email |
Email sending |
MemorySmsProvider |
alepha/sms |
SMS sending |
#Example: File System
1import { FileSystemProvider, MemoryFileSystemProvider } from "alepha/system"; 2 3const alepha = Alepha.create() 4 .with({ provide: FileSystemProvider, use: MemoryFileSystemProvider }); 5 6const fs = alepha.inject(MemoryFileSystemProvider); 7 8// Run code that writes files... 9await myService.generateReport();10 11// Assert with built-in helpers12expect(fs.wasWritten("/path/report.txt")).toBe(true);13expect(fs.wasWrittenMatching("/path/report.txt", /summary/)).toBe(true);14expect(fs.wasRead("/path/input.csv")).toBe(true);15expect(fs.wasDeleted("/path/temp.txt")).toBe(true);
#Example: Shell Commands
1import { ShellProvider, MemoryShellProvider } from "alepha/system"; 2 3const alepha = Alepha.create() 4 .with({ provide: ShellProvider, use: MemoryShellProvider }); 5 6const shell = alepha.inject(MemoryShellProvider); 7 8// Run code that executes shell commands... 9await myService.installDependencies();10 11expect(shell.wasCalled("yarn install")).toBe(true);
#Database Testing
Alepha uses real Postgres for tests. Each test file gets its own schema. Migrations run automatically before tests and the schema is dropped after tests complete.
The connection string is set in vitest.config.ts:
1env: {2 DATABASE_URL: "postgres://postgres:[email protected]:5432/postgres",3}
#TestProvider Pattern
To unit test protected methods on a class, create a test subclass that exposes them:
1class TestCliProvider extends CliProvider {2 public testParseFlags = this.parseFlags.bind(this);3 public testResolveCommand = this.resolveCommand.bind(this);4}5 6const alepha = Alepha.create();7const cli = alepha.inject(TestCliProvider);8const result = cli.testParseFlags(["--verbose"], flagDefs);