Testing & Code Quality
Testing Async Code
Async tests must wait for the work they assert on. Promise returns, async functions, fake timers, microtasks, cancellation, and unhandled rejections all affect test reliability.
- Return or await the async work
- Rejection paths need explicit assertions
- Fake timers control scheduled work
- Microtasks and timers are different queues
- Unhandled rejections should fail tests
it("loads user", async () => {
await expect(loadUser("u1")).resolves.toMatchObject({ id: "u1" })
})
it("rejects invalid id", async () => {
await expect(loadUser("")).rejects.toThrow("Invalid")
})import { afterEach, expect, it, vi } from "vitest"
afterEach(() => vi.useRealTimers())
it("debounces search", () => {
vi.useFakeTimers()
const fn = vi.fn()
const search = debounce(fn, 300)
search("a")
search("ab")
vi.advanceTimersByTime(299)
expect(fn).not.toHaveBeenCalled()
vi.advanceTimersByTime(1)
expect(fn).toHaveBeenCalledWith("ab")
})