---
title: Cancellable Test Resources | Recipes
---
# Cancellable Test Resources
A test can hold onto resources that don't stop when the test stops. A `fetch`, a child process, a file stream, a polling loop: none of those notice when Vitest has cancelled the test, and the worker has to sit there waiting for them to finish on their own. Vitest cancels a test when it exceeds its `timeout`, when another test fails under `--bail`, or when someone presses Ctrl+C in the terminal.
The test context provides a [`signal`](/guide/test-context#signal) 3.2.0 that fires in all of those cases. Pass it to anything that accepts an `AbortSignal` and the resource is released when Vitest cancels.
## Pattern
```ts
import { test } from 'vitest'
test('stop request when test times out', async ({ signal }) => {
await fetch('/heavy-resource', { signal })
}, 2000)
```
If the request hasn't completed within 2 seconds, `fetch` rejects with `AbortError` instead of the test hanging until the operation finishes.
## Other Web APIs that accept an `AbortSignal`
- [`fetch`](https://developer.mozilla.org/docs/Web/API/fetch)
- [`addEventListener`](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener), where passing `{ signal }` removes the listener on abort
- [`ReadableStream.pipeTo`](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo)
- Node.js APIs like [`fs.readFile`](https://nodejs.org/api/fs.html#fspromisesreadfilepath-options), [`child_process.spawn`](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options), and [`setTimeout` or `setInterval`](https://nodejs.org/api/timers.html), all of which accept `{ signal }`
- Any custom code that calls `signal.throwIfAborted()` or listens for `'abort'`
## Forwarding the signal
Wire the test's signal into your own helpers so cancellation propagates all the way down:
```ts
async function pollUntilReady(url: string, signal: AbortSignal) {
while (!signal.aborted) {
const res = await fetch(url, { signal })
if (res.ok) {
return
}
await new Promise(r => setTimeout(r, 200))
}
signal.throwIfAborted()
}
test('worker becomes ready', async ({ signal }) => {
await pollUntilReady('http://localhost:4000/health', signal)
}, 5000)
```
## See also
- [`signal` in Test Context](/guide/test-context#signal)
- [`bail`](/config/bail)
- [`testTimeout`](/config/testtimeout)