alepha@docs:~/docs/guides/testing$
cat 1-unit-tests.md
2 min read
Last commit:

#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:

bash
alepha init my-app --test

This installs Vitest and creates a vitest.config.ts with two test projects:

  • Node tests -- all *.spec.ts files (default environment)
  • Browser tests -- all *.browser.spec.ts / *.browser.spec.tsx files (jsdom environment)

Run tests:

bash
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.

typescript
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.

typescript
 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.

typescript
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

typescript
 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

typescript
 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, running via Docker (docker-compose). 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:

typescript
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:

typescript
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);