Skip to content

Testing Providers

You built a provider — now prove its lifecycle. test.provider gives each test a private scratch stack, so create / update / replace / delete runs never touch .alchemy/ or a shared stage.

Building the provider itself? Start at Build a custom provider.

test.provider(name, (stack) => effect) builds a scratch stack with a private in-memory state store, isolated from .alchemy/ and from sibling tests. Use it to exercise the create / update / replace / delete paths of a provider:

const { test } = Test.make({ providers: MyProvider.providers() });
test.provider(
"create, update, delete",
(stack) => Effect.gen(function* () {
// create
const v1 = yield* stack.deploy(
Effect.gen(function* () {
return yield* MyResource("Test", { name: "v1" });
}),
);
// update — same logical ID, new inputs
const v2 = yield* stack.deploy(
Effect.gen(function* () {
return yield* MyResource("Test", { name: "v2" });
}),
);
expect(v2.id).toBe(v1.id);
// destroy
yield* stack.destroy();
}),
);

For a full walkthrough with a real custom provider, see Build a custom provider → Test the lifecycle.

Inside the test body the configured providers Layer is already in scope, so out-of-band SDK calls (DynamoDB.describeTable, stripe.products.retrieve, …) work without extra setup — assert the cloud actually matches the resource’s reported outputs, as in the Stripe walkthrough.

Change a stables field (or whatever your diff flags as replace) between two stack.deploy calls with the same logical ID:

expect(v2.id).not.toBe(v1.id);
test + deploy(Stack)test.provider
State storestate option (default localState())private in-memory, per test
Survives runsyes (the point)no
Use caseend-to-end against a real stackprovider unit tests

For end-to-end tests against a real deployed Stack, see Testing a Stack.

test.provider’s in-memory state is exposed as the state field on ScratchStack for advanced cases. For simpler seeds, just call stack.deploy(...) once with the seed, then again with the actual test inputs — the second call sees the first call’s output as existing state.