close
logo
Rstest
指南
配置
API
English
简体中文
指南
配置
API
English
简体中文
logo
Rstest
Overview

Test API

Expect
Test
Describe
Hooks

Rstest Utility

Mock modules
Mock functions
MockInstance
Fake timers
Utilities
📝 在 GitHub 上编辑此页
上一页Overview
下一页Test

#Expect

expect 用于在测试中创建断言。Rstest 提供了丰富的 API 及匹配器,支持轮询、快照断言等。

你可以从 @rstest/core 包中导入 expect API:

import { expect, test } from '@rstest/core';

test('should add two numbers correctly', () => {
  expect(1 + 1).toBe(2);
  expect(1 + 2).toBe(3);
});

你也可以从 测试上下文 中获取 expect API,这有助于在并行测试中准确追踪断言的来源。

import { test } from '@rstest/core';

test('should add two numbers correctly', ({ expect }) => {
  expect(1 + 1).toBe(2);
  expect(1 + 2).toBe(3);
});

#expect

  • 类型: <T>(actual: T, message?: string) => Assertion<T>

为给定的值创建一个断言对象。

import { expect } from '@rstest/core';

expect(1 + 1).toBe(2);
expect('hello').toBeDefined();
expect([1, 2, 3]).toContain(2);

#expect.not

否定该断言。

expect(1 + 1).not.toBe(3);
expect('foo').not.toBeUndefined();

#expect.soft

  • 类型: <T>(actual: T, message?: string) => Assertion<T>

即使断言失败,测试也会继续执行,所有失败会在最后统一报告。

expect.soft(1 + 1).toBe(3); // 不会中断测试
expect.soft(1 + 2).toBe(4);

#expect.poll

  • 类型: <T>(actual: () => T, options?: { interval?: number, timeout?: number, message?: string }) => Promise<Assertion<T>>

轮询函数返回的值,直到断言通过或超时。

await expect.poll(() => getStatus()).toBe('ready');

#expect.unreachable

  • 类型: (message?: string) => never

标记代码路径为不可达。如果调用会抛出异常。

if (shouldNotHappen) {
  expect.unreachable('这里不应该被执行');
}

#expect.assertions

  • 类型: (expected: number) => void

验证在测试期间调用了特定数量的断言。常用来检查异步代码是否被调用。

expect.assertions(2);
expect(1 + 1).toBe(2);
expect(2 + 2).toBe(4);

#expect.hasAssertions

  • 类型: () => void

验证在测试期间至少调用了一个断言。

expect.hasAssertions();
expect(1 + 1).toBe(2);

#expect.addEqualityTesters

  • 类型: (testers: Array<Tester>) => void

自定义用来验证两个对象是否相等的测试器。

expect.addEqualityTesters([
  (a, b) => {
    if (typeof a === 'number' && typeof b === 'number') {
      return Math.abs(a - b) < 0.01;
    }
  },
]);
expect(0.1 + 0.2).toEqual(0.3); // 使用自定义测试器后为 true

#expect.addSnapshotSerializer

  • 类型: (serializer: SnapshotSerializer) => void

为快照测试添加自定义序列化工具。

expect.addSnapshotSerializer({
  test: (val) => typeof val === 'string' && val.startsWith('secret:'),
  print: (val) => '***MASKED***',
});
expect('secret:123').toMatchSnapshot(); // 快照输出的 secret 信息会被掩码

#expect.getState / expect.setState

  • 类型:
    • getState: () => MatcherState
    • setState: (state: Partial<MatcherState>) => void

获取或设置内部匹配器状态。

const state = expect.getState();
console.log(state.currentTestName);
expect.setState({ currentTestName: '自定义名称' });
console.log(expect.getState().currentTestName); // 输出 '自定义名称'

#匹配器(Matchers)

#常用匹配器

常用匹配器用于断言基本的值比较、类型检查和结构检查,涵盖了数字、字符串、对象、数组等日常断言需求。

  • toBe(value):使用 Object.is 检查严格相等。
  • toEqual(value):检查深度相等(递归检查所有字段)。
  • toStrictEqual(value):深度相等,包含 undefined 属性和稀疏数组。
  • toBeTruthy():检查值为真。
  • toBeFalsy():检查值为假。
  • toBeNull():检查值为 null。
  • toBeUndefined():检查值为 undefined。
  • toBeDefined():检查值不是 undefined。
  • toBeNaN():检查值为 NaN。
  • toBeGreaterThan(number):检查值大于给定数字。
  • toBeGreaterThanOrEqual(number):检查值大于等于给定数字。
  • toBeLessThan(number):检查值小于给定数字。
  • toBeLessThanOrEqual(number):检查值小于等于给定数字。
  • toBeCloseTo(number, numDigits?):检查数字接近另一个数字,可指定精度。
  • toContain(item):检查数组或字符串包含指定项。
  • toContainEqual(item):检查数组包含指定项(使用深度相等)。
  • toMatch(stringOrRegExp):检查字符串匹配正则或子串。
  • toMatchObject(object):检查对象包含指定属性子集。
  • toHaveLength(length):检查对象的 .length 属性等于指定值。
  • toHaveProperty(path, value?):检查对象指定路径上有属性,可选断言值。
  • toBeInstanceOf(class):检查值是指定类的实例。
  • toBeTypeOf(type):检查值类型(如 'string')。
  • toSatisfy(fn):检查值满足给定函数。
  • toBeOneOf(array):检查值在给定数组中。
  • toThrowError(expected?):检查函数抛出错误,可选匹配错误信息或类型。
// 相等性
expect(1 + 1).toBe(2);
expect({ a: 1 }).toEqual({ a: 1 });
expect([1, undefined]).toStrictEqual([1, undefined]);
expect(2).toBeOneOf([1, 2, 3]);

// 类型与定义
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect('foo').toBeDefined();
expect('bar').toBeTypeOf('string');
expect(new Date()).toBeInstanceOf(Date);
expect(NaN).toBeNaN();
expect('hello').toBeTruthy();
expect('').toBeFalsy();

// 数字比较
expect(5).toBeGreaterThan(3);
expect(5).toBeGreaterThanOrEqual(5);
expect(3).toBeLessThan(5);
expect(3).toBeLessThanOrEqual(3);
expect(0.1 + 0.2).toBeCloseTo(0.3, 5);

// 数组/对象/字符串
expect([1, 2, 3]).toContain(2);
expect([{ a: 1 }]).toContainEqual({ a: 1 });
expect('hello world').toMatch(/world/);
expect({ a: 1, b: 2 }).toMatchObject({ a: 1 });
expect('abc').toHaveLength(3);
expect({ foo: { bar: 1 } }).toHaveProperty('foo.bar', 1);

// 函数/异常
expect(() => {
  throw new Error('fail');
}).toThrowError('fail');
expect(3).toSatisfy((x) => x % 3 === 0 || x % 3 === 1);

#Mock 匹配器

Mock 匹配器用于断言 mock 函数(由 rstest.fn 或 rstest.spyOn 创建)的调用情况、返回值和调用顺序,是单元测试中验证函数交互的重要工具。

  • toHaveBeenCalled():检查 mock 函数至少被调用过一次。
  • toHaveBeenCalledTimes(times):检查 mock 函数被调用指定次数。
  • toHaveBeenCalledWith(...args):检查 mock 函数被指定参数调用。
  • toHaveBeenCalledBefore(mock):检查 mock 在另一个 mock 之前被调用。
  • toHaveBeenCalledAfter(mock):检查 mock 在另一个 mock 之后被调用。
  • toHaveBeenCalledExactlyOnceWith(...args):检查 mock 仅被指定参数调用过一次。
  • toHaveBeenLastCalledWith(...args):检查 mock 最后一次被指定参数调用。
  • toHaveBeenNthCalledWith(n, ...args):检查 mock 第 n 次被指定参数调用。
  • toHaveReturned():检查 mock 函数至少有一次返回。
  • toHaveReturnedTimes(times):检查 mock 函数返回指定次数。
  • toHaveReturnedWith(value):检查 mock 函数返回指定值。
  • toHaveLastReturnedWith(value):检查 mock 函数最后一次返回指定值。
  • toHaveNthReturnedWith(n, value):检查 mock 函数第 n 次返回指定值。
  • toHaveResolved():检查 Promise 至少 resolve 过一次。
  • toHaveResolvedTimes(times):检查 Promise resolve 指定次数。
  • toHaveResolvedWith(value):检查 Promise resolve 指定值。
  • toHaveLastResolvedWith(value):检查 Promise 最后一次 resolve 指定值。
  • toHaveNthResolvedWith(n, value):检查 Promise 第 n 次 resolve 指定值。
const mockFn = rstest.fn((x) => x + 1);
mockFn(1);

expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenCalledWith(1);

#快照匹配器

快照匹配器用于将值、错误或文件与之前记录的快照进行比较,便于追踪输出的变化。

  • toMatchSnapshot():将值与已保存的快照进行比较。
  • toMatchInlineSnapshot():将值与测试文件中的内联快照进行比较。
  • toThrowErrorMatchingSnapshot():检查抛出的错误与已保存快照匹配。
  • toThrowErrorMatchingInlineSnapshot():检查抛出的错误与内联快照匹配。
  • toMatchFileSnapshot(filepath):将值与指定文件中的快照进行比较。
expect('hello world').toMatchSnapshot();

expect(() => {
  throw new Error('fail');
}).toThrowErrorMatchingSnapshot();

await expect('hello world').toMatchFileSnapshot(
  '__snapshots__/file.output.txt',
);

#Promise 匹配器

  • resolves:对 Promise 的 resolve 结果进行断言。
  • rejects:对 Promise 的 reject 结果进行断言。
await expect(Promise.resolve('ok')).resolves.toBe('ok');
await expect(Promise.reject(new Error('fail'))).rejects.toThrow('fail');

#不对称匹配器

不对称匹配器是一些辅助工具,允许更灵活地匹配值,如部分匹配、类型匹配或模式匹配,适合编写更具表现力且不易碎的测试。

  • expect.anything():匹配除 null 和 undefined 之外的任意值。
  • expect.any(constructor):匹配指定类型的任意值。
  • expect.closeTo(number, precision?):匹配接近期望值的数字。
  • expect.arrayContaining(array):匹配包含期望元素的数组。
  • expect.objectContaining(object):匹配包含期望属性的对象。
  • expect.stringContaining(string):匹配包含期望子串的字符串。
  • expect.stringMatching(stringOrRegExp):匹配符合期望模式的字符串。
expect({ a: 1 }).toEqual({ a: expect.anything() });

expect(1).toEqual(expect.any(Number));

expect(0.1 + 0.2).toEqual(expect.closeTo(0.3, 5));

expect([1, 2, 3]).toEqual(expect.arrayContaining([2, 1]));

expect({ a: 1, b: 2 }).toEqual(expect.objectContaining({ a: 1 }));

expect('hello world').toEqual(expect.stringContaining('world'));

expect('hello world').toEqual(expect.stringMatching(/^hello/));

#自定义匹配器

你可以通过扩展 expect 添加自定义匹配器:

expect.extend({
  toBeDivisibleBy(received, argument) {
    const pass = received % argument === 0;
    if (pass) {
      return {
        message: () => `期望 ${received} 不能被 ${argument} 整除`,
        pass: true,
      };
    } else {
      return {
        message: () => `期望 ${received} 能被 ${argument} 整除`,
        pass: false,
      };
    }
  },
});

expect(10).toBeDivisibleBy(2);