| 1 | --- |
| 2 | title: Testing Providers |
| 3 | description: Exercise a provider's create, update, replace, and delete paths with test.provider and a scratch in-memory stack. |
| 4 | --- |
| 5 | |
| 6 | 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. |
| 7 | |
| 8 | Building the provider itself? Start at [Build a custom provider](/infrastructure-as-code/custom-provider). |
| 9 | |
| 10 | ## Scratch stacks with test.provider |
| 11 | |
| 12 | `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: |
| 13 | |
| 14 | ```typescript |
| 15 | const { test } = Test.make({ providers: MyProvider.providers() }); |
| 16 | |
| 17 | test.provider( |
| 18 | "create, update, delete", |
| 19 | (stack) => Effect.gen(function* () { |
| 20 | // create |
| 21 | const v1 = yield* stack.deploy( |
| 22 | Effect.gen(function* () { |
| 23 | return yield* MyResource("Test", { name: "v1" }); |
| 24 | }), |
| 25 | ); |
| 26 | |
| 27 | // update — same logical ID, new inputs |
| 28 | const v2 = yield* stack.deploy( |
| 29 | Effect.gen(function* () { |
| 30 | return yield* MyResource("Test", { name: "v2" }); |
| 31 | }), |
| 32 | ); |
| 33 | expect(v2.id).toBe(v1.id); |
| 34 | |
| 35 | // destroy |
| 36 | yield* stack.destroy(); |
| 37 | }), |
| 38 | ); |
| 39 | ``` |
| 40 | |
| 41 | For a full walkthrough with a real custom provider, see [Build a custom provider → Test the lifecycle](/infrastructure-as-code/custom-provider#test-the-lifecycle). |
| 42 | |
| 43 | ## Assert against the real cloud |
| 44 | |
| 45 | 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](/infrastructure-as-code/custom-provider#test-the-lifecycle). |
| 46 | |
| 47 | ## Verify replacement |
| 48 | |
| 49 | Change a `stables` field (or whatever your `diff` flags as `replace`) between two `stack.deploy` calls with the same logical ID: |
| 50 | |
| 51 | ```typescript |
| 52 | expect(v2.id).not.toBe(v1.id); |
| 53 | ``` |
| 54 | |
| 55 | ## Scratch state vs persistent state |
| 56 | |
| 57 | | | `test` + `deploy(Stack)` | `test.provider` | |
| 58 | |---|---|---| |
| 59 | | State store | `state` option (default `localState()`) | private in-memory, per test | |
| 60 | | Survives runs | yes (the point) | no | |
| 61 | | Use case | end-to-end against a real stack | provider unit tests | |
| 62 | |
| 63 | For end-to-end tests against a real deployed Stack, see [Testing a Stack](/testing/testing-a-stack). |
| 64 | |
| 65 | ## Seed pre-existing resources |
| 66 | |
| 67 | `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. |
| 68 | |
| 69 | ## Where next |
| 70 | |
| 71 | - [Build a custom provider](/infrastructure-as-code/custom-provider) — the provider this page tests, end to end. |
| 72 | - [Providers](/infrastructure-as-code/provider) — the operation contract under test. |
| 73 | - [Test harness](/testing/test-harness) — everything `Test.make` returns. |
| 74 | - [Testing](/testing) — the full harness reference. |