---
name: test-api
description: test/it function for defining tests with modifiers
---

# Test API

## Basic Test

```ts
import { expect, test } from 'vitest';

test('adds numbers', () => {
  expect(1 + 1).toBe(2);
});

// Alias: it
import { it } from 'vitest';

it('works the same', () => {
  expect(true).toBe(true);
});
```

## Async Tests

```ts
test('async test', async () => {
  const result = await fetchData();
  expect(result).toBeDefined();
});

// Promises are automatically awaited
test('returns promise', () => {
  return fetchData().then((result) => {
    expect(result).toBeDefined();
  });
});
```

## Test Options

```ts
// Timeout (default: 5000ms)
test('slow test', async () => {
  // ...
}, 10_000);

// Or with options object
test('with options', { timeout: 10_000, retry: 2 }, async () => {
  // ...
});
```

## Test Modifiers

### Skip Tests

```ts
test.skip('skipped test', () => {
  // Won't run
});

// Conditional skip
test.skipIf(process.env.CI)('not in CI', () => {});
test.runIf(process.env.CI)('only in CI', () => {});

// Dynamic skip via context
test('dynamic skip', ({ skip }) => {
  skip(someCondition, 'reason');
  // ...
});
```

### Focus Tests

```ts
test.only('only this runs', () => {
  // Other tests in file are skipped
});
```

### Todo Tests

```ts
test.todo('implement later');

test.todo('with body', () => {
  // Not run, shows in report
});
```

### Failing Tests

```ts
test.fails('expected to fail', () => {
  expect(1).toBe(2); // Test passes because assertion fails
});
```

### Concurrent Tests

```ts
// Run tests in parallel
test.concurrent('test 1', async ({ expect }) => {
  // Use context.expect for concurrent tests
  expect(await fetch1()).toBe('result');
});

test.concurrent('test 2', async ({ expect }) => {
  expect(await fetch2()).toBe('result');
});
```

### Sequential Tests

```ts
// Force sequential in concurrent context
test.sequential('must run alone', async () => {});
```

## Parameterized Tests

### test.each

```ts
test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) = %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// With objects
test.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
])('add($a, $b) = $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});

// Template literal
test.each`
  a    | b    | expected
  ${1} | ${1} | ${2}
  ${1} | ${2} | ${3}
`('add($a, $b) = $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});
```

### test.for

Preferred over `.each` - doesn't spread arrays:

```ts
test.for([
  [1, 1, 2],
  [1, 2, 3],
])('add(%i, %i) = %i', ([a, b, expected], { expect }) => {
  // Second arg is TestContext
  expect(a + b).toBe(expected);
});
```

## Test Context

First argument provides context utilities:

```ts
test('with context', ({ expect, skip, task }) => {
  console.log(task.name); // Test name
  skip(someCondition); // Skip dynamically
  expect(1).toBe(1); // Context-bound expect
});
```

## Custom Test with Fixtures

```ts
import { test as base } from 'vitest';

const test = base.extend({
  db: async ({}, use) => {
    const db = await createDb();
    await use(db);
    await db.close();
  },
});

test('query', async ({ db }) => {
  const users = await db.query('SELECT * FROM users');
  expect(users).toBeDefined();
});
```

## Retry Configuration

```ts
test('flaky test', { retry: 3 }, async () => {
  // Retries up to 3 times on failure
});

// Advanced retry options
test(
  'with delay',
  {
    retry: {
      count: 3,
      delay: 1000,
      condition: /timeout/i, // Only retry on timeout errors
    },
  },
  async () => {},
);
```

## Tags

```ts
test('database test', { tags: ['db', 'slow'] }, async () => {});

// Run with: vitest --tags db
```

## Key Points

- Tests with no body are marked as `todo`
- `test.only` throws in CI unless `allowOnly: true`
- Use context's `expect` for concurrent tests and snapshots
- Function name is used as test name if passed as first arg

<!--
Source references:
- https://vitest.dev/api/test.html
-->
