Alchemy resources are easy to test since they’re just functions, but Alchemy also offers a simple alchemy.test utility to help isolate your Scopes for each test suite.
Test Setup
Section titled “Test Setup”Import alchemy’s test utility and your resource:
import { describe, expect } from "bun:test";import alchemy, { destroy } from "alchemy";import { Database } from "../src/neon/database";
// make sure to augment `alchemy` by importing your preferred testing utilityimport "alchemy/test/bun";Test Scope Creation
Section titled “Test Scope Creation”Create a test function at the top of your test suite:
// Create test scope using filenameconst test = alchemy.test(import.meta, { prefix: BRANCH_PREFIX});We pass import.meta so that all the resources created in this test suite will be isolated from other tests.
Resource Test Implementation
Section titled “Resource Test Implementation”Now, create a test as you ordinarily would:
test("create, update, and delete database", async (scope) => { // ..});Note how our test is passed a scope value - we’ll use that at the end to clean up our resources.
Inside our test, we can simple create and update our resources, make assertions, etc.:
// Create resourcelet database = await Database(testId, { name: `${testId}-db`, // Other required properties...});
// Test assertionsexpect(database.id).toBeTruthy();
// Update resourcedatabase = await Database(testId, { // Updated properties...});Finally, wrap all of this in a try-finally so that we can ensure our test resources are cleaned up.
try { // (create, update and assertions)} finally { // delete all resources await destroy(scope);
// Verify resource was deleted if you want to}Integration Testing with Vitest Global Setup
Section titled “Integration Testing with Vitest Global Setup”For integration tests where you want to deploy infrastructure once before all tests run and clean up after, use Vitest’s global setup feature.
Configure Vitest
Section titled “Configure Vitest”Create a vitest.config.ts that references your global setup file:
import { defineConfig } from "vitest/config";
export default defineConfig({ test: { include: ["test/**/*.test.ts"], globalSetup: "./vitest.setup.ts", },});Create Global Setup
Section titled “Create Global Setup”Create a vitest.setup.ts that deploys your infrastructure and provides context to tests:
/// <reference types="vitest" />
import type { TestProject } from "vitest/node";
export async function setup({ provide }: TestProject) { // Import and run your alchemy app const { app, worker } = await import("./alchemy.run.ts");
if (!worker.url) { throw new Error("worker.url is not defined"); }
// Provide values to your tests provide("workerUrl", worker.url);
// Return cleanup function return async () => { await app.cleanup(); };}
// Declare the provided context for type safetydeclare module "vitest" { export interface ProvidedContext { workerUrl: string; }}Write Tests
Section titled “Write Tests”Use inject() to access the provided context in your tests:
import { describe, expect, inject, it } from "vitest";
describe("worker", () => { it("should return the correct response", async () => { const workerUrl = inject("workerUrl"); const response = await fetch(workerUrl); expect(response.status).toBe(200); expect(await response.text()).toBe("Ok"); });});