alepha@docs:~/docs/guides/testing$
cat 1-unit-tests.md | pretty
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. 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);