accelint-react-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Testing Best Practices

React组件测试最佳实践

Expert guidance for writing maintainable, user-centric React component tests with Testing Library. Focused on query selection, accessibility-first testing, and avoiding implementation details.
使用Testing Library编写可维护、以用户为中心的React组件测试的专家指南。重点关注查询选择、可访问性优先测试,以及避免实现细节。

NEVER Do When Writing React Tests

编写React测试时绝对不要做的事

  • NEVER query by test IDs before trying accessible queries - Test IDs bypass accessibility verification: a button with
    data-testid="submit"
    but no accessible name works in tests but fails for screen reader users. When tests pass with test IDs, you ship inaccessible UIs. Query hierarchy:
    getByRole
    >
    getByLabelText
    >
    getByText
    >
    getByTestId
    . Each step down this list means less confidence your UI is usable.
  • NEVER use
    fireEvent
    for user interactions when
    userEvent
    is available
    -
    fireEvent
    dispatches single DOM events, missing the event sequence real users trigger:
    fireEvent.click()
    fires one click event, but real users trigger focus → mousedown → mouseup → click. Components that work with fireEvent break in production when users interact normally.
    userEvent.click()
    simulates the full interaction sequence, catching bugs fireEvent misses.
  • NEVER test implementation details instead of user behavior - Tests that verify "state variable X equals Y" or "function Z was called" create false failures: you refactor from useState to useReducer, all tests fail, yet the UI works identically. Testing implementation details punishes refactoring and provides zero confidence the user experience works. Test what users see and do (rendered output, interaction results), not how your component achieves it internally.
  • NEVER query from
    container
    or use destructured queries after initial render
    -
    const { getByText } = render(<Component />)
    creates stale queries that miss updates: after state changes, destructured queries search the initial DOM snapshot, missing newly rendered elements. This causes "element not found" errors for elements that are actually present. Always use
    screen.getByText()
    which automatically queries the current DOM state. Using screen consistently also makes tests more maintainable - adding a new query doesn't require updating the destructuring.
  • NEVER add aria-label or role attributes solely for tests - If you're adding
    aria-label="submit-button"
    or
    role="button"
    just so tests can find elements, you're working backwards. Tests should verify the component is already accessible, not make it accessible for tests. Adding test-only ARIA pollutes production code and masks real accessibility problems. Fix the component's semantic HTML and existing ARIA first.
  • NEVER snapshot entire component trees without specific assertions - Massive snapshots with 500+ lines break on any change (updated classname, new prop, reordered elements), forcing reviewers to approve diffs they can't meaningfully evaluate. When test failures require "just update the snapshot" without understanding why, the test has zero value. Snapshot specific critical structures (error messages, data tables) with targeted assertions for everything else.
  • NEVER use
    waitFor
    for actions that return promises
    -
    waitFor(() => expect(element).toBeInTheDocument())
    polls repeatedly until timeout when a promise-based
    findBy
    query solves it in one shot:
    await screen.findByText('loaded')
    waits for the element to appear without polling. Reserve waitFor for assertions that can't use findBy (checking element disappears, waiting for attribute changes).
  • NEVER perform side effects inside waitFor callback -
    waitFor(() => { fireEvent.click(button); expect(text).toBeInTheDocument(); })
    runs the click multiple times as waitFor retries, causing unpredictable behavior. waitFor is for waiting on assertions, not triggering actions. Perform all actions outside waitFor, then use waitFor only for the assertion:
    fireEvent.click(button); await waitFor(() => expect(text).toBeInTheDocument());
    or better yet,
    await userEvent.click(button); expect(await screen.findByText(text)).toBeInTheDocument();
    .
  • NEVER create custom renders without documenting provider requirements - A custom
    renderWithRedux
    function with undocumented required store shape breaks for every developer: they call
    render(<Component />)
    instead of
    renderWithRedux()
    , tests fail with cryptic "Cannot read property of undefined", wasting 15 minutes debugging. Centralize provider setup in test utils with TypeScript types that enforce correct usage, or document required wrappers prominently.
  • NEVER mix queries from different Testing Library imports - Importing both
    @testing-library/react
    render and
    @testing-library/dom
    queries creates confusion:
    screen
    from react package doesn't work with
    getByRole
    from dom package, causing "screen.getByRole is not a function" errors. Import all queries from
    @testing-library/react
    for React components - it re-exports everything from dom with React-specific enhancements.
  • 绝对不要在尝试可访问查询之前使用test ID查询 - Test ID会绕过可访问性验证:一个带有
    data-testid="submit"
    但没有可访问名称的按钮在测试中可以正常工作,但对屏幕阅读器用户来说是失效的。当使用test ID的测试通过时,你交付的是不可访问的UI。查询层级:
    getByRole
    >
    getByLabelText
    >
    getByText
    >
    getByTestId
    。每往下走一步,意味着你对UI可用性的信心就越低。
  • 当userEvent可用时,绝对不要使用fireEvent模拟用户交互 -
    fireEvent
    只会触发单个DOM事件,缺少真实用户触发的事件序列:
    fireEvent.click()
    只会触发一个点击事件,但真实用户会触发focus → mousedown → mouseup → click的序列。在fireEvent下能正常工作的组件,在生产环境中用户正常交互时可能会失效。
    userEvent.click()
    会模拟完整的交互序列,能捕捉到fireEvent遗漏的bug。
  • 绝对不要测试实现细节而非用户行为 - 验证“状态变量X等于Y”或“函数Z被调用”的测试会产生虚假失败:当你从useState重构为useReducer时,所有测试都会失败,但UI的工作方式完全相同。测试实现细节会阻碍重构,并且无法为用户体验的有效性提供任何保障。测试用户看到和操作的内容(渲染输出、交互结果),而不是组件内部的实现方式。
  • 绝对不要从container查询或在初始渲染后使用解构查询 -
    const { getByText } = render(<Component />)
    会创建过时的查询,无法捕获更新:状态变化后,解构的查询只会搜索初始DOM快照,无法找到新渲染的元素。这会导致“元素未找到”的错误,而实际上元素是存在的。始终使用
    screen.getByText()
    ,它会自动查询当前DOM状态。持续使用screen还能让测试更易于维护——添加新查询时无需更新解构语句。
  • 绝对不要仅为测试添加aria-label或role属性 - 如果你只是为了让测试能找到元素而添加
    aria-label="submit-button"
    role="button"
    ,那你搞反了顺序。测试应该验证组件本身已经具备可访问性,而不是为了测试让组件变得可访问。添加仅用于测试的ARIA属性会污染生产代码,并掩盖真实的可访问性问题。首先修复组件的语义化HTML和现有ARIA属性。
  • 绝对不要在没有特定断言的情况下快照整个组件树 - 包含500多行代码的大型快照会因任何变化(更新类名、新增属性、元素重排序)而失效,迫使评审者批准他们无法有效评估的差异。当测试失败只需要“更新快照”而无需理解原因时,这个测试毫无价值。针对特定关键结构(错误消息、数据表格)进行快照,对其他内容使用针对性断言。
  • 绝对不要对返回Promise的操作使用waitFor -
    waitFor(() => expect(element).toBeInTheDocument())
    会重复轮询直到超时,而基于Promise的findBy查询可以一次性解决问题:
    await screen.findByText('loaded')
    会等待元素出现,无需轮询。仅在无法使用findBy的断言场景中使用waitFor(比如检查元素消失、等待属性变化)。
  • 绝对不要在waitFor回调中执行副作用 -
    waitFor(() => { fireEvent.click(button); expect(text).toBeInTheDocument(); })
    会在waitFor重试时多次触发点击,导致不可预测的行为。waitFor用于等待断言,而不是触发操作。所有操作都应该在waitFor之外执行,然后仅使用waitFor进行断言:
    fireEvent.click(button); await waitFor(() => expect(text).toBeInTheDocument());
    或者更好的方式:
    await userEvent.click(button); expect(await screen.findByText(text)).toBeInTheDocument();
  • 绝对不要创建未记录提供者要求的自定义渲染函数 - 一个未记录所需store结构的自定义
    renderWithRedux
    函数会让所有开发者陷入困境:他们调用
    render(<Component />)
    而不是
    renderWithRedux()
    ,测试会因模糊的“无法读取undefined的属性”错误而失败,浪费15分钟调试时间。将提供者设置集中在测试工具中,使用TypeScript类型强制正确用法,或者显著记录所需的包装器。
  • 绝对不要混合使用来自不同Testing Library导入的查询 - 同时导入
    @testing-library/react
    的render和
    @testing-library/dom
    的查询会造成混淆:来自react包的screen无法与来自dom包的
    getByRole
    配合使用,会导致“screen.getByRole is not a function”错误。对于React组件,所有查询都应该从
    @testing-library/react
    导入——它会重新导出dom包的所有内容,并添加React特定的增强功能。

Before Writing Tests, Ask

编写测试前要问的问题

Apply these thinking patterns before implementing React component tests:
在实现React组件测试前,应用以下思考模式:

Query Selection Strategy

查询选择策略

  • Which query matches how users find this element? Real users don't look for test IDs or CSS classes - they look for labels, buttons, headings. If you can't query by role or label, your UI lacks accessibility. Query difficulty reveals UX problems before they reach production.
  • Does this element need to be found at all? Not every element needs a query assertion. Users don't verify "loading spinner exists" - they verify "data appears after loading". Test outcomes, not intermediate states.
  • Should I use getBy, queryBy, or findBy? Start with getBy for immediate presence - it gives the best error messages. Use queryBy only when asserting absence (.not.toBeInTheDocument()). Use findBy for async appearance. Never use queryBy + expect(...).toBeInTheDocument() - use getBy instead for better error messages when the element is missing.
  • 哪种查询符合用户找到该元素的方式? 真实用户不会寻找test ID或CSS类——他们会寻找标签、按钮、标题。如果你无法通过角色或标签查询,说明你的UI缺乏可访问性。查询的难度会在问题进入生产环境前揭示UX问题。
  • 这个元素真的需要被查询吗? 不是每个元素都需要查询断言。用户不会验证“加载 spinner 存在”——他们会验证“加载后数据出现”。测试结果,而不是中间状态。
  • 我应该使用getBy、queryBy还是findBy? 首先使用getBy检查即时存在的元素——它能提供最佳错误信息。仅在断言元素不存在时使用queryBy(.not.toBeInTheDocument())。使用findBy处理异步出现的元素。绝对不要使用queryBy + expect(...).toBeInTheDocument()——改用getBy,这样在元素缺失时能获得更好的错误信息。

User vs Implementation Testing

用户测试 vs 实现测试

  • What would a user do to verify this works? Users click buttons and read text - they don't check state variables or mock function calls. If your test uses
    rerender()
    or accesses component internals, you're testing implementation. Refactor to test through user actions.
  • Will this test survive a refactoring that doesn't change behavior? If renaming a function or switching from useState to useReducer breaks the test, you're testing implementation details. These tests waste time blocking safe changes while providing no confidence the UI actually works.
  • 用户会如何验证这个功能正常工作? 用户会点击按钮、阅读文本——他们不会检查状态变量或模拟函数调用。如果你的测试使用
    rerender()
    或访问组件内部,那你就是在测试实现细节。重构测试,通过用户操作进行验证。
  • 在不改变行为的重构后,这个测试还能通过吗? 如果重命名函数或从useState切换到useReducer会导致测试失败,那你就是在测试实现细节。这些测试会浪费时间阻碍安全的变更,同时无法为UI的有效性提供任何保障。

Async and Timing

异步与计时

  • Is this query for something that loads asynchronously? Use
    findBy*
    for anything loaded via useEffect, API calls, or setTimeout.
    getBy*
    throws immediately if element is missing;
    findBy*
    waits for it to appear. Using getBy for async content creates race conditions that only fail in CI.
  • Am I waiting for an element to appear or disappear? Appearance =
    findBy*
    query. Disappearance =
    waitForElementToBeRemoved
    . State changes =
    waitFor
    with assertion. Each has different semantics; using the wrong one causes flaky tests or longer timeouts.
  • 这个查询是针对异步加载的内容吗? 对于通过useEffect、API调用或setTimeout加载的内容,使用
    findBy*
    getBy*
    会在元素缺失时立即抛出错误;
    findBy*
    会等待元素出现。对异步内容使用getBy会导致竞态条件,只会在CI环境中失败。
  • 我是在等待元素出现还是消失? 出现 = 使用
    findBy*
    查询。消失 = 使用
    waitForElementToBeRemoved
    。状态变化 = 使用
    waitFor
    配合断言。每种场景都有不同的语义;使用错误的方式会导致测试不稳定或超时时间过长。

Test Isolation and Setup

测试隔离与设置

  • Does this component need context providers to render? Components using useContext, Redux hooks, or React Router throw without providers. Create custom render utilities that wrap components in required providers automatically. Repeating provider setup in every test file is a maintenance disaster.
  • What's the minimal setup needed for this test case? Tests with excessive setup (mocking 10 functions for a button test) are fragile and slow. Mock only external dependencies (APIs, localStorage), never your own functions. If setup is complex, the component design might be the problem.
  • 这个组件需要上下文提供者才能渲染吗? 使用useContext、Redux hooks或React Router的组件在没有提供者时会抛出错误。创建自动将组件包裹在所需提供者中的自定义渲染工具。在每个测试文件中重复设置提供者是维护灾难。
  • 这个测试用例所需的最小设置是什么? 包含过多设置的测试(为按钮测试模拟10个函数)既脆弱又缓慢。只模拟外部依赖(API、localStorage),不要模拟自己的函数。如果设置过于复杂,可能是组件设计存在问题。

How to Use

使用方法

This skill uses progressive disclosure to minimize context usage:
本技能使用渐进式披露来最小化上下文使用:

1. Start with the Overview (AGENTS.md)

1. 从概述开始(AGENTS.md)

Read AGENTS.md for a concise overview of all rules with one-line summaries.
阅读AGENTS.md获取所有规则的简洁概述,每条规则都有一行总结。

2. Load Specific Rules as Needed

2. 根据需要加载特定规则

Use these explicit triggers to know when to load each reference file:
MANDATORY Loading (load entire file):
  • Writing any query (getBy, findBy, queryBy*)** → query-priority.md
  • Simulating user interactions (clicks, typing, etc.)user-events.md
Load When You See These Patterns:
  • Confusion about getBy vs findBy vs queryByquery-variants.md
  • waitFor, async queries, or "act" warningsasync-testing.md
  • Components using Context, Redux, Routercustom-render.md
  • Testing accessibility or ARIA attributesaccessibility-queries.md
  • Using container, wrapper, or rerender extensivelyanti-patterns.md
  • Queries failing or can't find right selectorquery-variants.md for screen.debug() usage
Do NOT Load Unless Specifically Needed:
  • Do NOT load custom-render.md for simple components without providers
  • Do NOT load async-testing.md for synchronous tests
  • Do NOT load accessibility-queries.md unless testing ARIA or a11y concerns
使用以下明确的触发条件来确定何时加载每个参考文件:
强制加载(加载整个文件):
  • 编写任何查询(getBy, findBy, queryBy*)** → query-priority.md
  • 模拟用户交互(点击、输入等)user-events.md
看到以下模式时加载:
  • 对getBy、findBy、queryBy的区别感到困惑query-variants.md
  • 使用waitFor、异步查询或遇到“act”警告async-testing.md
  • 组件使用Context、Redux、Routercustom-render.md
  • 测试可访问性或ARIA属性accessibility-queries.md
  • 大量使用container、wrapper或rerenderanti-patterns.md
  • 查询失败或找不到合适的选择器query-variants.md 了解screen.debug()的用法
除非特别需要,否则不要加载:
  • 对于没有使用提供者的简单组件,不要加载custom-render.md
  • 对于同步测试,不要加载async-testing.md
  • 除非测试ARIA或可访问性问题,否则不要加载accessibility-queries.md

3. Apply the Pattern

3. 应用模式

Each reference file contains:
  • ❌ Incorrect examples showing the anti-pattern
  • ✅ Correct examples showing the optimal implementation
  • Explanations of why the pattern matters
每个参考文件包含:
  • ❌ 展示反模式的错误示例
  • ✅ 展示最佳实现的正确示例
  • 解释该模式的重要性

4. Audit Existing Tests (Optional)

4. 审计现有测试(可选)

Use the provided scripts to audit existing test suites:
bash
undefined
使用提供的脚本来审计现有测试套件:
bash
undefined

Check query priority (testId usage, container.querySelector)

检查查询优先级(testId使用、container.querySelector)

./scripts/check-query-priority.sh
./scripts/check-query-priority.sh

Find fireEvent that should be userEvent

查找应替换为userEvent的fireEvent

./scripts/find-fire-event.sh
./scripts/find-fire-event.sh

Detect deprecated wrapper/container patterns

检测已弃用的wrapper/container模式

./scripts/detect-wrapper-queries.sh
undefined
./scripts/detect-wrapper-queries.sh
undefined

5. Use the Report Template

5. 使用报告模板

When this skill is invoked for test code review, use the standardized report format:
Template:
assets/output-report-template.md
The report format provides:
  • Executive Summary with accessibility confidence and user-centric coverage assessment
  • Severity levels (Critical, High, Medium, Low) for prioritization
  • Impact analysis (accessibility confidence, user-centric confidence, test reliability, refactor safety)
  • Categorization (Query Priority, Query Variants, User Events, Async Testing, Custom Render, Accessibility, Anti-patterns)
  • Pattern references linking to detailed guidance in references/
  • Summary table for tracking all issues
When to use the report template:
  • Skill invoked directly via
    /accelint-react-testing <path>
  • User asks to "review test code" or "audit tests" across file(s), invoking skill implicitly
When NOT to use the report template:
  • User asks to "write a test for this function" (direct implementation)
  • User asks "what's wrong with this test?" (answer the question)
  • User requests specific test fixes (apply fixes directly without formal report)
当本技能被调用来进行测试代码评审时,使用标准化报告格式:
模板:
assets/output-report-template.md
报告格式包含:
  • 执行摘要,包含可访问性信心和以用户为中心的覆盖评估
  • 严重性级别(Critical、High、Medium、Low)用于优先级排序
  • 影响分析(可访问性信心、以用户为中心的信心、测试可靠性、重构安全性)
  • 分类(查询优先级、查询变体、用户事件、异步测试、自定义渲染、可访问性、反模式)
  • 指向references/中详细指南的模式参考
  • 用于跟踪所有问题的摘要表格
何时使用报告模板:
  • 通过
    /accelint-react-testing <path>
    直接调用技能
  • 用户要求“评审测试代码”或“审计测试”跨文件时,隐式调用技能
何时不使用报告模板:
  • 用户要求“为这个函数编写测试”(直接实现)
  • 用户问“这个测试有什么问题?”(直接回答问题)
  • 用户要求特定的测试修复(直接应用修复,无需正式报告)

What This Skill Covers

本技能涵盖的内容

Expert guidance on React Testing Library patterns:
  1. Query Priority - Accessible query hierarchy from getByRole to getByTestId
  2. Query Variants - When to use getBy, findBy, queryBy for different scenarios
  3. User Events - userEvent vs fireEvent for realistic interaction testing
  4. Async Testing - Handling promises, waitFor, findBy queries, avoiding act warnings
  5. Custom Render - Setting up providers (Context, Redux, Router) for complex components
  6. Accessibility Queries - Testing with roles, labels, and ARIA attributes
  7. Anti-patterns - Avoiding implementation details, container usage, excessive snapshots
  8. Audit Scripts - Automated detection of suboptimal patterns in existing tests
关于React Testing Library模式的专家指南:
  1. 查询优先级 - 从getByRole到getByTestId的可访问查询层级
  2. 查询变体 - 在不同场景下何时使用getBy、findBy、queryBy
  3. 用户事件 - userEvent vs fireEvent用于真实交互测试
  4. 异步测试 - 处理Promise、waitFor、findBy查询,避免act警告
  5. 自定义渲染 - 为复杂组件设置提供者(Context、Redux、Router)
  6. 可访问性查询 - 使用角色、标签和ARIA属性进行测试
  7. 反模式 - 避免实现细节、container使用、过度快照
  8. 审计脚本 - 自动检测现有测试中的次优模式

Query Selection Decision Tree

查询选择决策树

Use this hierarchy when selecting queries - try options from top to bottom:
1. getByRole          ← Preferred: Accessible, reflects how users & ATs interact
   ↓ Can't find role?
   
2. getByLabelText     ← For form fields: matches how users read forms
   ↓ No label?
   
3. getByPlaceholderText  ← For inputs: less accessible than labels
   ↓ No placeholder?
   
4. getByText          ← For non-interactive content: headings, paragraphs
   ↓ Text not unique?
   
5. getByDisplayValue  ← For form inputs: current value
   ↓ No display value?
   
6. getByAltText       ← For images: alt attribute
   ↓ No alt text?
   
7. getByTitle         ← For title attribute: less accessible
   ↓ No title?
   
8. getByTestId        ← Last resort: no accessibility verification
Key principles:
  • Higher queries = more confidence in accessibility
  • If you can't query by role/label, fix the component's accessibility first
  • getByTestId means "I've verified accessibility is impossible here"
选择查询时使用以下层级——从上到下尝试选项:
1. getByRole          ← 首选:可访问,反映用户和辅助技术的交互方式
   ↓ 找不到角色?
   
2. getByLabelText     ← 适用于表单字段:匹配用户阅读表单的方式
   ↓ 没有标签?
   
3. getByPlaceholderText  ← 适用于输入框:比标签的可访问性差
   ↓ 没有占位符?
   
4. getByText          ← 适用于非交互式内容:标题、段落
   ↓ 文本不唯一?
   
5. getByDisplayValue  ← 适用于表单输入框:当前值
   ↓ 没有显示值?
   
6. getByAltText       ← 适用于图片:alt属性
   ↓ 没有alt文本?
   
7. getByTitle         ← 适用于title属性:可访问性较差
   ↓ 没有title?
   
8. getByTestId        ← 最后手段:无法验证可访问性
核心原则:
  • 层级越高的查询,对可访问性的信心越强
  • 如果你无法通过角色/标签查询,首先修复组件的可访问性
  • 使用getByTestId意味着“我已经验证此处无法实现可访问性”

Important Notes

重要说明

  • The
    screen
    export is not magic
    - It's just
    getQueriesForElement(document.body)
    . Using
    screen.getByRole()
    is identical to destructured
    getByRole()
    from render, but screen never goes stale after re-renders.
  • Testing Library encourages accessibility by making accessible elements easiest to query - If queries are hard, your UI is hard to use. Query difficulty is a UX code smell.
  • Use screen.debug() or screen.logTestingPlaygroundURL() when queries fail - When getByRole fails, run
    screen.debug()
    to see the current DOM or
    screen.logTestingPlaygroundURL()
    to get an interactive tool showing what queries work. Don't guess at selectors - let Testing Library show you what's available.
  • queryBy returns null silently - use getBy for better errors - When an element should exist,
    getBy*
    throws with helpful suggestions about similar elements and available roles.
    queryBy*
    returns null, requiring you to add your own assertion with less helpful error output. Use queryBy only when asserting absence with .not.toBeInTheDocument().
  • Act warnings mean React state updates happened outside Testing Library's awareness - Usually caused by promises resolving after test completion or missing
    await
    on async queries. Not caused by correct use of findBy or waitFor.
  • userEvent methods are async (return promises), fireEvent methods are sync - Forgetting
    await userEvent.click()
    causes "act" warnings and flaky tests as state updates happen after assertions run.
  • screen导出并非魔法 - 它只是
    getQueriesForElement(document.body)
    。使用
    screen.getByRole()
    与从render中解构的
    getByRole()
    效果相同,但screen在重渲染后不会过时。
  • Testing Library通过让可访问元素最易于查询来鼓励可访问性 - 如果查询难度大,说明你的UI使用难度大。查询难度是UX的警示信号。
  • 当查询失败时,使用screen.debug()或screen.logTestingPlaygroundURL() - 当getByRole失败时,运行
    screen.debug()
    查看当前DOM,或运行
    screen.logTestingPlaygroundURL()
    获取交互式工具,查看可用的查询。不要猜测选择器——让Testing Library为你展示可用的选项。
  • queryBy会静默返回null - 使用getBy获取更好的错误信息 - 当元素应该存在时,
    getBy*
    会抛出错误,并提供关于相似元素和可用角色的有用建议。
    queryBy*
    会返回null,需要你添加自己的断言,且错误输出的帮助性较差。仅在使用.not.toBeInTheDocument()断言元素不存在时使用queryBy。
  • Act警告意味着React状态更新发生在Testing Library的感知之外 - 通常由Promise在测试完成后resolve或异步查询缺少
    await
    导致。正确使用findBy或waitFor不会导致该警告。
  • userEvent方法是异步的(返回Promise),fireEvent方法是同步的 - 忘记
    await userEvent.click()
    会导致“act”警告和测试不稳定,因为状态更新会在断言执行后发生。