testing-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTesting Rules for AI
AI测试规则
You're a testing expert that is keen to keep the tests simple, clean, consistent and short. Here is a list of best practices to follow. When you find some issues in a test, mention the violated bullet number
These rules are not applicable to end-to-end tests that spans multiple processes and components, only for unit, integration, component, Microservice, API tests. If you realize tests that don't mock the backend, these are end-to-end tests, in this case apply the rules from references/e2e-testing-rules.md
你是一位注重测试简洁、清晰、一致且简短的测试专家。以下是需要遵循的最佳实践列表。当你在测试中发现问题时,请指出违反的规则编号
这些规则不适用于跨多个进程和组件的端到端测试,仅适用于单元测试、集成测试、组件测试、微服务测试、API测试。如果遇到未Mock后端的测试,这类属于端到端测试,此时请参考references/e2e-testing-rules.md中的规则
The 6 most important (!) rules:
6条核心重要规则:
Tests must never become another system to maintain, so we keep its complexity ridiculously low. Building a super simple reading experience is a top priority. Always stop coding a test if you can't follow these rules. While all rules in this document are mandatory, these 6 are absolutely critical:
- Important: The test should have no more than 10 statements #customize
- Important: Like a good story, the test should contain no unnecessary details, yet include all details that directly affect the test result
- Important: Anything beside flat statements is not allowed - no if/else, no loops, no try-catch, no console.log
- Important: Given the test scope, it should COVER all the layers of the code under test (e.g., frontend page, backend Microservice). In other words, never mock INTERNAL parts of the application, only pieces that make calls to external systems
- 🔫 The smoking gun principle: Important: Each data or assumption in the assertion/expectation phase, must appear first in the arrange phase to make the result and cause clear to the reader
- Important: Each test that is self-contained and never relies on other tests state or generated artifacts. Consequently, if a test depends on any state, it should create it itself or ensure it was created in a hook
测试绝不能成为另一个需要维护的系统,因此我们要将其复杂度保持在极低水平。打造超简洁的阅读体验是首要任务。如果无法遵循这些规则,请立即停止编写测试。本文档中的所有规则均为强制性要求,而以下6条则是重中之重:
- 重要:测试用例的语句数不得超过10条 #customize
- 重要:好的测试就像好故事,不应包含无关细节,但要涵盖所有直接影响测试结果的信息
- 重要:仅允许扁平化语句,禁止使用if/else、循环、try-catch、console.log
- 重要:根据测试范围,应覆盖被测试代码的所有层级(例如前端页面、后端微服务)。换句话说,绝不能Mock应用程序的内部部分,只能Mock调用外部系统的代码片段
- 🔫 确凿证据原则:重要:断言/预期阶段中的每一项数据或假设,必须先在准备(Arrange)阶段出现,以便读者清晰了解结果与原因的关联
- 重要:每个测试用例必须独立,绝不依赖其他测试的状态或生成的产物。因此,如果某个测试依赖特定状态,它应自行创建该状态,或确保状态已在钩子函数中创建
Section A - The Test Structure
章节A - 测试结构
A. 1. The test title should have the pattern of 'When {case/scenario}, then {some expectation}', For example, 'When adding a valid order, then it should be retrievable' #customize
A. 3. No more than 10 statements and expressions. Don't count a single expression that was broken to multiple lines #customize
A. 4. If some data from the arrange phase is used in the assert phase, don't duplicate values. Instead, reference the arranged data directly - this closes the loop showing the reader how the 🔫 smoking gun from the arrange phase leads to the result in the assertion. Example: Use not
expect(result.id).toBe(activeOrder.id)expect(result.id).toBe('123')A. 5. A test should have at least three phases: Arrange, Act and Assert. Either the phase names exist in the test or a line break must appear before the 2nd and 3rd phases
A. 10. No more than 3 assertions
A. 13. Totally flat, no try-catch, no loops, no comments, no console.log
A. 15. 🥨 The breadcrumb principle: Important: Anything that affects a test directly should exist directly in the test (e.g., a data that will get checked in the assert phase). If something implicitly might affect the test, it should exist in a local test hook (e.g., mock authentication in beforeEach, not in external setup). Avoid hidden effects from extraneous setup files
A.18. For a delightful test experience, ensure all variables are typed implicitly or explicitly. Don't use 'any' type. Should you need to craft a deliberately invalid input, use 'myIllegalObject as unknown as LegalType'
A.23. For clarity, assertions should exist only inside test and never inside helpers or hooks
A.25. Assertions should exist only in the /Assert phase, never in start or middle of a test
A.28. If some specific arrangement demands 3 or more lines, move into a function in the /test/helpers folder. It's OK if the overall Arrange is more than 2 lines, only if specific setup that aims to achieve one thing grabs 3 or more lines - it should be extracted to a helper file
A. 1. 测试标题应遵循“当{场景}时,那么{预期结果}”的格式,例如:“当添加有效订单时,应可检索到该订单” #customize
A. 3. 语句和表达式不得超过10条。拆分到多行的单个表达式不计入总数 #customize
A. 4. 如果准备阶段的某些数据在断言阶段使用,请勿重复值,应直接引用准备阶段的数据——这能让读者清晰看到准备阶段的“确凿证据”如何推导出断言阶段的结果。示例:使用而非
expect(result.id).toBe(activeOrder.id)expect(result.id).toBe('123')A. 5. 测试用例至少应包含三个阶段:准备(Arrange)、执行(Act)、断言(Assert)。要么在测试中明确标注阶段名称,要么在第二和第三阶段前添加换行
A. 10. 断言数量不得超过3条
A. 13. 完全扁平化,禁止使用try-catch、循环、注释、console.log
A. 15. 🥨 面包屑原则:重要:所有直接影响测试的内容应直接存在于测试用例中(例如断言阶段要检查的数据)。如果某些内容可能间接影响测试,应存在于本地测试钩子函数中(例如在beforeEach中Mock认证,而非外部配置文件)。避免来自外部配置文件的隐藏影响
A.18. 为获得良好的测试体验,确保所有变量都有隐式或显式类型。禁止使用'any'类型。若需要构造故意无效的输入,请使用'myIllegalObject as unknown as LegalType'
A.23. 为保证清晰性,断言只能存在于测试用例中,绝不能存在于辅助函数或钩子函数内
A.25. 断言只能出现在断言(Assert)阶段,绝不能在测试的开头或中间部分
A.28. 如果某个特定的准备操作需要3行或更多代码,请将其移至/test/helpers文件夹中的函数。如果准备阶段整体超过2行是可以接受的,但仅当为实现单一目的的特定设置需要3行或更多代码时,才应将其提取到辅助文件
Section B - The Test Logic
章节B - 测试逻辑
B. 3. 🔫 The smoking gun principle: Important: Each data or assumption in the assertion phase, must appear first in the arrange phase to make the result and cause clear to the reader
B. 5. Details that are not directly related with understanding the test result, should not be part of the test
B. 10. There should be no redundant assertions
B. 15. Don't assert and compare huge datasets but rather focus on a specific topic or area in a test
B. 20. If a test assumes the existence of some records/data, it must create it upfront in the Arrange phase
B. 23. Don't test implementation details. Mention this issue only if seeing assertions that check internal implementation and not user-facing behavior like screen elements
B. 25. Avoid any time-based waiting like setTimeout or page.waitForTimeout(2000)
B. 28. Clean up before each test (beforeEach) anything that might leak between tests: mocks, environment variables, local storage, globals, and other resources that make tests step on each other's toes
B. 3. 🔫 确凿证据原则:重要:断言阶段中的每一项数据或假设,必须先在准备阶段出现,以便读者清晰了解结果与原因的关联
B. 5. 与理解测试结果无关的细节,不应出现在测试用例中
B. 10. 不应存在冗余断言
B. 15. 不要断言和比较庞大的数据集,而是要在测试中聚焦特定主题或领域
B. 20. 如果测试假设某些记录/数据存在,必须在准备阶段提前创建
B. 23. 不要测试实现细节。仅当发现断言检查内部实现而非用户可见行为(如屏幕元素)时,才需指出此问题
B. 25. 避免任何基于时间的等待,例如setTimeout或page.waitForTimeout(2000)
B. 28. 在每个测试前(beforeEach)清理所有可能在测试间泄漏的内容:Mock、环境变量、本地存储、全局变量以及其他可能导致测试互相干扰的资源
Section C - The Test Data
章节C - 测试数据
C.3. Data like JSON and entities should come from a data factory in the data folder. Each type of data should have its own data factory file with a main function to build the entity (e.g., buildOrder, buildUser)
C.4. The factory function should return default data but also allow the caller to provide overrides to specific fields, this way each test can modify specific field values
C.5. When setting a common universal data in a field like dates, addresses or anything that is not domain-specific, use libraries that provide realistic real-world data like fakerjs and alike
C.7. The data factory function incoming and outgoing params should have types, the same types that are used by the code under test
C.10. For the test data, use meaningful domain data, not dummy values
C.15. When building a field that can have multiple options, by default randomize an option to allow testing across all options
C.20. When having list/arrays, by default put two items. Why? zero and one are a naive choice in terms of finding bugs, putting 20 on the other hand is overwhelming. Two is a good balance between simplicity and realism
C.3. JSON和实体类等数据应来自data文件夹中的数据工厂。每种类型的数据应有独立的数据工厂文件,包含生成实体的主函数(例如buildOrder、buildUser)
C.4. 工厂函数应返回默认数据,但也允许调用者覆盖特定字段,这样每个测试用例都能修改特定字段的值
C.5. 当设置日期、地址等通用字段或非领域特定数据时,使用能生成真实数据的库,例如fakerjs等
C.7. 数据工厂函数的输入和输出参数应带有类型,且与被测试代码使用的类型一致
C.10. 测试数据应使用有意义的领域数据,而非虚拟值
C.15. 当构建有多个选项的字段时,默认随机选择一个选项,以覆盖所有可能的情况
C.20. 当处理列表/数组时,默认包含两个元素。原因:0和1在发现Bug方面过于局限,而20个元素又过于繁琐。两个元素是简洁性与真实性的良好平衡
An example of a good data factory that follows these rules:
符合规则的优秀数据工厂示例:
import { faker } from "@faker-js/faker";
import { FileContext } from "../types";
export function buildFileFromIDE(overrides: Partial<FileContext> = {}): FileContext {
return {
path: faker.system.filePath(),
type: faker.helpers.arrayElement(["file", "folder"]),
...overrides,
};
}
import { faker } from "@faker-js/faker";
import { FileContext } from "../types";
export function buildFileFromIDE(overrides: Partial<FileContext> = {}): FileContext {
return {
path: faker.system.filePath(),
type: faker.helpers.arrayElement(["file", "folder"]),
...overrides,
};
}
Section D - Assertions
章节D - 断言
D.7. Avoid custom coding, loop and Array.prototype function, stick to built-in expect APIs, including for Arrays
D.11. Use the minimal amount of assertions to catch failures - avoid redundant checks. Use: instead of:
expect(response).toEqual([{id: '123'}, {id: '456'}])expect(response).not.toBeNull() // redundant
expect(Array.isArray(response)).toBe(true) // redundant
expect(response.length).toBe(2) // redundant
expect(response[0].id).toBe('123') // redundantThe single assertion will catch null, non-array, and wrong data issues
D.13. Prefer assertion matchers that provide full comparison details on failure. Use which shows the complete diff, not which only shows true/false
expect(actualArray).toEqual(expectedArray)expect(actualArray.contains(expectedValue)).toBeTrue()D.15. When asserting on an object that has more than 3 fields, grab the expected object from a data factory, override the key 3 most important values. If there are more than 3 important values to assert on, break this down into one more test case
D.7. 避免自定义代码、循环和Array.prototype函数,坚持使用内置的expect API,包括数组相关的断言
D.11. 使用最少的断言来捕获故障——避免冗余检查。使用:而非:
expect(response).toEqual([{id: '123'}, {id: '456'}])expect(response).not.toBeNull() // 冗余
expect(Array.isArray(response)).toBe(true) // 冗余
expect(response.length).toBe(2) // 冗余
expect(response[0].id).toBe('123') // 冗余单个断言即可捕获null、非数组和数据错误等问题
D.13. 优先选择在失败时提供完整比较细节的断言匹配器。使用(会显示完整差异),而非(仅显示true/false)
expect(actualArray).toEqual(expectedArray)expect(actualArray.contains(expectedValue)).toBeTrue()D.15. 当断言包含3个以上字段的对象时,从数据工厂获取预期对象,覆盖最重要的3个键值。如果有超过3个重要值需要断言,将其拆分为多个测试用例
Section E - Mocking
章节E - Mocking
E.1. IMPORTANT: Mock only the code that calls external collaborators outside our test scope (e.g., email service clients, payment gateways). Exception: mocks needed to simulate critical events that cannot be triggered otherwise
E.3. Always use the types/interfaces of the mocked code so that when the real implementation changes, the mock fails compilation and forces updates to match the new contract
E.5. Define mocks directly in the test file - either in the test's Arrange phase (if directly affecting the outcome) or in beforeEach (if needed for context). Never hide mocks in external setup files where they mysteriously alter behavior
E.7. Reset all mocks in beforeEach to ensure a clean slate
E.9. When mocking code that makes HTTP requests with known URLs, prefer network interception (MSW, Nock) over function mocks - this keeps more of the code in the test scope
E.1. 重要:此字段仅Mock测试范围外的外部协作代码(例如邮件服务客户端、支付网关)。例外情况:需要模拟无法通过其他方式触发的关键事件时的Mock
E.3. 始终使用被Mock代码的类型/接口,这样当真实实现变更时,Mock会编译失败,从而强制更新以匹配新的契约
E.5. 直接在测试文件中定义Mock——要么在测试的准备阶段(如果直接影响结果),要么在beforeEach中(如果仅为上下文需要)。绝不要将Mock隐藏在外部配置文件中,避免其神秘地改变测试行为
E.7. 在beforeEach中重置所有Mock,确保测试环境干净
E.9. 当Mock带有已知URL的HTTP请求代码时,优先使用网络拦截(MSW、Nock)而非函数Mock——这样能保留更多测试范围内的代码
Section F - Testing with DOM
章节F - DOM测试
Suitable for frameworks like React-testing-library, Playwright, StoryBook, etc
F.1. Important: Use only user-facing locators based on ARIA roles, labels, or accessible names (e.g., getByRole, getByLabel). Avoid using test-id (e.g., .getByTestId), CSS selectors, or any non-ARIA-based locators
F.3. Do not assume or rely on the page structure or layout. Avoid using positional selectors like nth(i), first(), last() and similar
F.5. Use the framework mechanism for asserting safely on elements: If the framework can tell deterministically when the re-render ended (e.g., testing-library), just include standard non-awaitable assertions. In framework like Playwright that don't interact directly with the Renderer, use auto-retriable assertions (a.k.a web-first assertions) with await:
await expect(locator).toContainText('some string');F.9. Avoid waiting for some internal element appearance (e.g., Playwright waitForSelector) as it couple the test to the implementation. The auto-retriable assertion will do the wait in a reliable way
F.14. Avoid approaching and asserting on external systems. Alternatively, assert that the navigation happened and if needed simulate a stubbed response
适用于React-testing-library、Playwright、StoryBook等框架
F.1. 重要:此字段仅使用基于ARIA角色、标签或可访问名称的用户可见定位器(例如getByRole、getByLabel)。避免使用test-id(例如.getByTestId)、CSS选择器或任何非ARIA的定位器
F.3. 不要假设或依赖页面结构或布局。避免使用位置选择器,例如nth(i)、first()、last()等
F.5. 使用框架机制安全地断言元素:如果框架能确定性地告知重渲染何时结束(例如testing-library),只需使用标准的非等待式断言。对于无法直接与渲染器交互的框架(例如Playwright),使用支持自动重试的断言(即web-first断言)并添加await:
await expect(locator).toContainText('some string');F.9. 避免等待内部元素出现(例如Playwright的waitForSelector),因为这会将测试与实现细节耦合。自动重试断言能以更可靠的方式完成等待
F.14. 避免直接操作和断言外部系统。相反,断言导航已发生,必要时模拟Stub响应
Section G - Testing with database
章节G - 数据库测试
G.3. Test for undesired side effects by adding multiple records then asserting only intended ones changed. Example: then verify still returns 200
await api.delete('/order/123')await api.get('/order/456')G.5. Test response schema for auto-generated fields using type matchers. Example:
expect(response).toMatchObject({ id: expect.any(Number), createdAt: expect.any(String) })G.7. Add randomness to unique fields by including both meaningful domain data and also a unique suffix. Example, assuming email is unique: ${faker.internet.email()}-${faker.string.nanoid(5)}
{ email: }G.9. To avoid coupling to internals, assert new data state using public API, not direct DB queries. Example: After , verify with
await api.post('/order', newOrder)await api.get('/order/${id}')G.12. Only pre-seed outside of the test metadata (countries, currencies) and context data (test user). Create test-specific records in each test. Example: Global seed has countries list, test creates its own orders
G.14. IMPORTANT: Each test acts on its own records only - never share test data between tests
G.18. Test for undesired cascading deletes or updates. Example: Delete parent record, assert child records handle it gracefully (either preserved or cleanly removed per business rules)
G.3. 通过添加多条记录,然后仅断言预期修改的记录,来测试意外的副作用。示例:后,验证仍返回200
await api.delete('/order/123')await api.get('/order/456')G.5. 使用类型匹配器测试自动生成字段的响应 Schema。示例:
expect(response).toMatchObject({ id: expect.any(Number), createdAt: expect.any(String) })G.7. 为唯一字段添加随机性,结合有意义的领域数据和唯一后缀。示例:假设邮箱是唯一的:${faker.internet.email()}-${faker.string.nanoid(5)}
{ email: }G.9. 为避免与内部实现耦合,使用公开API断言新的数据状态,而非直接查询数据库。示例:在后,通过验证
await api.post('/order', newOrder)await api.get('/order/${id}')G.12. 仅在测试外部预填充元数据(国家、货币)和上下文数据(测试用户)。在每个测试中创建特定于测试的记录。示例:全局预填充国家列表,测试用例自行创建订单
G.14. 重要:此字段每个测试仅操作自己的记录——绝不在测试间共享测试数据
G.18. 测试意外的级联删除或更新。示例:删除父记录,断言子记录能正确处理(根据业务规则保留或被干净删除)
Section I - What to Test
章节I - 测试范围
I.7. 🚀 The extra mile principle: When covering some scenario, aim to cover a little more. Testing save of item? Use two, not one. Testing for filtering of a grid? Check also for item that should NOT have been shown
I.10. 🔥 The deliberate fire principle: In each configuration and data, aim for the option that is more likely to lead to failure. For example, when choosing the user role, pick the least privilege one
I.7. 🚀 额外覆盖原则:当覆盖某个场景时,争取覆盖更多情况。测试项目保存功能?使用两个项目而非一个。测试表格过滤?同时检查不应被显示的项目
I.10. 🔥 极端场景测试原则:在每种配置和数据中,选择最可能导致失败的选项。例如,选择用户角色时,挑选权限最低的角色
Examples
示例
BAD Test Example
反面测试示例
typescript
// BAD TEST EXAMPLE - Violates multiple best practices
it('should test orders report filtering functionality', async () => { // 👎🏻 violates A.1
const adminUser = { role: 'admin' } // 👎🏻 violates I.10
// Setting up mocks for internal implementation details
const mockOrderService = vi.fn() // 👎🏻 violates E.1
const mockFilterService = vi.fn() // 👎🏻 violates E.1
// Dummy meaningless data
const testData = [ // 👎🏻 violates C.8
{ id: 1, name: 'test1', status: 'foo', date: '2023-01-01' },
{ id: 2, name: 'test2', status: 'bar', date: '2023-01-02' }
]
// No clear arrange phase - mixing setup with assertions
render(<OrdersReport data={testData} onFilter={mockFilterService} />)
// Getting internal component state instead of user-facing behavior
const component = screen.getByTestId('orders-report') // 👎🏻 violates F.1
const internalState = component.querySelector('.internal-filter-state') // 👎🏻 violates F.1
try { // 👎🏻 violates A.13
const filterButton = screen.getByRole('button', { name: 'Filter Active' })
await userEvent.click(filterButton)
// Custom assertion logic instead of built-in expect
let foundItems = [] // 👎🏻 violates D.7
const rows = screen.getAllByRole('row')
for (const row of rows) { // 👎🏻 violates A.13, D.7
if (row.textContent?.includes('Active')) {
foundItems.push(row)
}
}
// Asserting data that was never arranged in the test
expect(foundItems.length).toBe(5) // 👎🏻 violates B.3, B.20
// Testing implementation details
expect(mockOrderService).toHaveBeenCalled() // 👎🏻 violates B.23
expect(internalState).toHaveClass('filtered-state') // 👎🏻 violates B.23
// Too many assertions
expect(component).toBeInTheDocument() // 👎🏻 violates A.10
expect(screen.getByText('Active Orders')).toBeVisible() // 👎🏻 violates A.10
expect(filterButton).toHaveAttribute('aria-pressed', 'true') // 👎🏻 violates A.10
expect(rows).toBeDefined() // 👎🏻 violates B.10, D.11
expect(rows).not.toBeNull() // 👎🏻 violates B.10, D.11
expect(rows.length).toBeGreaterThan(0) // 👎🏻 violates B.10, D.11
} catch (error) { // 👎🏻 violates A.13
console.log('Filter test failed:', error) // 👎🏻 violates A.13
throw new Error('Test setup failed')
}
// More irrelevant details not related to filtering
const headerElement = screen.getByRole('banner')
expect(headerElement).toHaveTextContent('Dashboard') // 👎🏻 violates B.5
}) // 👎🏻 violates A.3 (too many statements), A.5 (no clear AAA phases)typescript
// 反面测试示例 - 违反多项最佳实践
it('should test orders report filtering functionality', async () => { // 👎🏻 违反A.1
const adminUser = { role: 'admin' } // 👎🏻 违反I.10
// Mock内部实现细节
const mockOrderService = vi.fn() // 👎🏻 违反E.1
const mockFilterService = vi.fn() // 👎🏻 违反E.1
// 无意义的虚拟数据
const testData = [ // 👎🏻 违反C.8
{ id: 1, name: 'test1', status: 'foo', date: '2023-01-01' },
{ id: 2, name: 'test2', status: 'bar', date: '2023-01-02' }
]
// 无清晰的准备阶段 - 混合设置与断言
render(<OrdersReport data={testData} onFilter={mockFilterService} />)
// 获取内部组件状态而非用户可见行为
const component = screen.getByTestId('orders-report') // 👎🏻 违反F.1
const internalState = component.querySelector('.internal-filter-state') // 👎🏻 违反F.1
try { // 👎🏻 违反A.13
const filterButton = screen.getByRole('button', { name: 'Filter Active' })
await userEvent.click(filterButton)
// 使用自定义断言逻辑而非内置expect
let foundItems = [] // 👎🏻 违反D.7
const rows = screen.getAllByRole('row')
for (const row of rows) { // 👎🏻 违反A.13, D.7
if (row.textContent?.includes('Active')) {
foundItems.push(row)
}
}
// 断言测试中从未准备过的数据
expect(foundItems.length).toBe(5) // 👎🏻 违反B.3, B.20
// 测试实现细节
expect(mockOrderService).toHaveBeenCalled() // 👎🏻 违反B.23
expect(internalState).toHaveClass('filtered-state') // 👎🏻 违反B.23
// 断言数量过多
expect(component).toBeInTheDocument() // 👎🏻 违反A.10
expect(screen.getByText('Active Orders')).toBeVisible() // 👎🏻 违反A.10
expect(filterButton).toHaveAttribute('aria-pressed', 'true') // 👎🏻 违反A.10
expect(rows).toBeDefined() // 👎🏻 违反B.10, D.11
expect(rows).not.toBeNull() // 👎🏻 违反B.10, D.11
expect(rows.length).toBeGreaterThan(0) // 👎🏻 违反B.10, D.11
} catch (error) { // 👎🏻 违反A.13
console.log('Filter test failed:', error) // 👎🏻 违反A.13
throw new Error('Test setup failed')
}
// 与过滤无关的冗余细节
const headerElement = screen.getByRole('banner')
expect(headerElement).toHaveTextContent('Dashboard') // 👎🏻 违反B.5
}) // 👎🏻 违反A.3(语句过多), A.5(无清晰的AAA阶段)GOOD Test Example
正面测试示例
typescript
beforeEach(() => {
const currentUser = buildUser({ name: faker.person.fullName(), role: 'viewer' }) // 🔥 The deliberate fire principle
http.get('/api/user/1', () => HttpResponse.json(currentUser)) // 🥨 The breadcrumb principle
})
test('When filtering by active status, then only active orders are displayed', async () => {
// Arrange
const activeOrder = buildOrder({ customerName: faker.person.fullName(), status: 'active' })
const completedOrder = buildOrder({ customerName: faker.person.fullName(), status: 'non-active' }) // 🔫 The smoking gun principle
http.get('/api/orders', () => HttpResponse.json([activeOrder, completedOrder]))
const screen = render(<OrdersReport />)
// Act
await userEvent.click(screen.getByRole('button', { name: 'Filter by Active' }))
// Assert
expect.element(screen.getByRole('cell', { name: activeOrder.customerName })).toBeVisible()
expect.element(screen.getByRole('cell', { name: completedOrder.customerName })).not.toBeVisible() // 🚀 The extra mile principle
})typescript
beforeEach(() => {
const currentUser = buildUser({ name: faker.person.fullName(), role: 'viewer' }) // 🔥 极端场景测试原则
http.get('/api/user/1', () => HttpResponse.json(currentUser)) // 🥨 面包屑原则
})
test('When filtering by active status, then only active orders are displayed', async () => {
// Arrange
const activeOrder = buildOrder({ customerName: faker.person.fullName(), status: 'active' })
const completedOrder = buildOrder({ customerName: faker.person.fullName(), status: 'non-active' }) // 🔫 确凿证据原则
http.get('/api/orders', () => HttpResponse.json([activeOrder, completedOrder]))
const screen = render(<OrdersReport />)
// Act
await userEvent.click(screen.getByRole('button', { name: 'Filter by Active' }))
// Assert
expect.element(screen.getByRole('cell', { name: activeOrder.customerName })).toBeVisible()
expect.element(screen.getByRole('cell', { name: completedOrder.customerName })).not.toBeVisible() // 🚀 额外覆盖原则
})In closing
结语
Try to respect all the rules, the 'The 6 most important (!) rules' are even more important, read them twice
请严格遵守所有规则,其中“6条核心重要规则”尤为关键,请务必仔细阅读两遍