#Unit Tests
Alepha uses Vitest as the test runner. Every alepha init scaffolds a Vitest config and a sample test file — Vitest ships embedded in alepha, so there is nothing to install. All tests run with globals: true, so you do not need to import test, expect, or describe.
#Setup
Every Alepha project is scaffolded with test support — no flag needed:
alepha init my-app
init writes a vitest.config.ts (it pins test.root so a parent monorepo config can't take over) and a starter test/dummy.spec.ts. Specs live in test/, named *.spec.ts.
Run tests with alepha test. Positional arguments are forwarded to Vitest as filename/test filters:
alepha test # All tests
alepha test auth # Only specs matching "auth"
alepha test test/user.spec.ts # A single file
#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);