# Keep test subscriptions lean

Clean up test subscriptions to reduce unnecessary activity and preserve capacity for live workloads.

Stripe test resources share infrastructure with live resources. For example, the same systems cycle subscriptions, generate invoices, and send webhooks. Keeping your active test subscription count lean reduces unnecessary background activity, which helps preserve capacity and performance for live workloads.

Automated tests can leave subscriptions active for a long time after a test run finishes. As test suites and development workflows scale, these subscriptions continue cycling, generating invoices, and sending events. Design each test to create only the resources it needs and remove them as soon as the test finishes.

## Use test clocks instead of separate subscriptions 

Use [test clocks](https://docs.stripe.com/billing/testing/test-clocks.md) to test subscription behavior that depends on time, including renewals, trials, prorations, and payment retries. A test clock lets you move one set of associated objects through multiple lifecycle states instead of creating a separate subscription for every state you need to test.

Follow the [test clock API workflow](https://docs.stripe.com/billing/testing/test-clocks/api-advanced-usage.md) to create a simulation, add a customer and subscription, advance time, and monitor the resulting changes. The amount of time you can advance depends on the shortest billing interval in the simulation. See [advance the simulated time](https://docs.stripe.com/billing/testing/test-clocks/api-advanced-usage.md#advance-clock) for details.

Delete the simulation during teardown. Deleting it also deletes its associated test customers and cancels their subscriptions, preventing those resources from continuing to generate activity after the test.

```curl
curl -X DELETE https://api.stripe.com/v1/test_helpers/test_clocks/{{CLOCK_ID}} \
  -u "<<YOUR_SECRET_KEY>>:"
```

Review the current [test clock limits and restrictions](https://docs.stripe.com/billing/testing/test-clocks/api-advanced-usage.md#restrictions) when designing parallel tests.

## Cancel subscriptions during teardown 

When a test creates a subscription without a test clock, record its ID immediately and cancel it in a teardown hook. Keep the IDs scoped to the current test run so cleanup doesn’t affect subscriptions created by another test or developer.

This Jest example attempts every cancellation even if the test fails or one cancellation returns an error:

```javascript
let createdSubscriptionIds = [];

afterEach(async () => {
  const subscriptionIds = createdSubscriptionIds;
  createdSubscriptionIds = [];

  const results = await Promise.allSettled(
    subscriptionIds.map((id) => stripe.subscriptions.cancel(id)),
  );
  const failures = results.filter((result) => result.status === 'rejected');

  if (failures.length > 0) {
    throw new Error(`Failed to cancel ${failures.length} test subscriptions`);
  }
});

test('creates a subscription', async () => {
  const subscription = await stripe.subscriptions.create({
    customer: testCustomerId,
    items: [{price: recurringPriceId}],
  });
  createdSubscriptionIds.push(subscription.id);

  expect(subscription.id).toBeDefined();
});
```

Register each object as soon as its create request succeeds. If setup creates other objects, such as `Customer` objects, track and remove those objects in dependency order. Use your test framework’s teardown hooks or a `finally` block so cleanup runs after assertion and setup failures.

## Add metadata for backup cleanup 

Teardown is the primary way to limit active test resources. A process interruption can prevent teardown from running, so add metadata that identifies the test suite and CI run that created each subscription.

```curl
curl https://api.stripe.com/v1/subscriptions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d customer={{CUSTOMER_ID}} \
  -d "items[0][price]={{RECURRING_PRICE_ID}}" \
  -d "metadata[created_by]=billing_ci_suite" \
  -d "metadata[ci_run]={{CI_RUN_ID}}"
```

Run a scheduled cleanup job that searches for your suite’s metadata and cancels only subscriptions older than a threshold you define. Choose a threshold that exceeds the longest expected test run, and record cleanup failures for investigation.

If the Search API isn’t available for your account, paginate through the [list Subscriptions API](https://docs.stripe.com/api/subscriptions/list.md) instead. In your cleanup worker, filter the returned subscriptions by their metadata and creation time before canceling them. Use a test API key so the worker can’t affect live subscriptions. See [search query syntax](https://docs.stripe.com/search.md#query-fields-for-subscriptions) and the [Subscription cancellation API](https://docs.stripe.com/api/subscriptions/cancel.md).

## Test webhook handlers without persistent subscriptions 

When you only need to test webhook routing, signature handling, or application behavior, use the [Stripe CLI](https://docs.stripe.com/cli.md) to forward fixture events to your local webhook handler.

Start your local server, then run the listener in one terminal. Replace `localhost:4242/webhook` with your local webhook URL:

```bash
stripe listen --forward-to localhost:4242/webhook
```

Configure your handler to verify signatures with the signing secret printed by the listener. Keep the listener running, then trigger the fixture from another terminal:

```bash
stripe trigger invoice.payment_succeeded
```

This approach tests your handler without requiring your test to maintain an active subscription. The CLI creates fixture data for the event, which might not correspond to a subscription your test created. Use an end-to-end subscription test when you need to verify the complete sequence of API changes and event notifications. See [test subscription webhook notifications](https://docs.stripe.com/billing/testing.md#webhooks) for other testing options.

## Keep ongoing test activity lean 

- Use test clocks to move one set of resources through time instead of creating subscriptions for every lifecycle state.
- Cancel subscriptions and remove related resources after each test, including when setup or assertions fail.
- Add run-specific metadata and schedule backup cleanup for interrupted test runs.
- Use mocks or fakes when a test doesn’t need to call Stripe.
- Use Stripe CLI fixtures when you only need to exercise webhook-handler behavior.

These practices prevent test subscriptions from cycling after they serve their purpose, limiting unnecessary invoice generation and webhook delivery on infrastructure shared with live resources.

## See also

- [Test your Billing integration](https://docs.stripe.com/billing/testing.md)
- [Use test clocks](https://docs.stripe.com/billing/testing/test-clocks.md)
- [Set up automated testing](https://docs.stripe.com/automated-testing.md)
