test-namer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTest Namer
Test Namer
Write tests that describe behavior in plain English, not implementation details. Based on Vladimir Khorikov's testing principles.
编写用通俗易懂的英语描述行为而非实现细节的测试,本指南基于Vladimir Khorikov的测试原则编写。
Naming Guidelines
命名规范
Rules
规则
- No rigid naming policy — never use or similar templates
[MethodUnderTest]_[Scenario]_[ExpectedResult] - Describe behavior to a non-programmer familiar with the problem domain — a domain expert should understand the test name
- Separate words with underscores in function/method names (not needed in string-based test names like JS/TS)
- Do not include the SUT's method name in the test name — test behavior, not a method
- Use plain facts, not wishes — write , not
is_invalidshould_be_invalid - Use basic English grammar — articles (,
a) improve readabilitythe - Be specific — beats
Delivery_with_a_past_date_is_invalidDelivery_with_invalid_date_is_invalid
- 不使用僵化的命名模板 —— 永远不要使用或类似的模板
[MethodUnderTest]_[Scenario]_[ExpectedResult] - 向熟悉业务领域的非程序员描述行为 —— 领域专家应该能看懂测试名称
- 函数/方法名称中用下划线分隔单词(JS/TS等基于字符串的测试名称不需要这么做)
- 测试名称中不要包含SUT的方法名 —— 测试的是行为,不是某个方法
- 使用客观事实而非主观预期 —— 写,不要写
is_invalidshould_be_invalid - 使用基础英语语法 —— 冠词(、
a)可以提升可读性the - 尽可能具体 —— 优于
Delivery_with_a_past_date_is_invalidDelivery_with_invalid_date_is_invalid
Naming Progression Example
命名优化示例
Starting from a rigid convention — progressively improve:
IsDeliveryValid_InvalidDate_ReturnsFalse -- rigid, cryptic
Delivery_with_invalid_date_should_be_invalid -- plain English (good start)
Delivery_with_past_date_should_be_invalid -- more specific
Delivery_with_past_date_is_invalid -- fact, not wish
Delivery_with_a_past_date_is_invalid -- natural grammar (final)从僵化的约定开始,逐步优化:
IsDeliveryValid_InvalidDate_ReturnsFalse -- 僵化、晦涩
Delivery_with_invalid_date_should_be_invalid -- 通俗英语(良好的开端)
Delivery_with_past_date_should_be_invalid -- 更具体
Delivery_with_past_date_is_invalid -- 客观事实,而非预期
Delivery_with_a_past_date_is_invalid -- 符合自然语法(最终版本)Exception: Utility Code
例外:工具类代码
For utility/helper code without business logic, referencing the method name is acceptable since the behavior doesn't mean anything to business people:
Sum_of_two_numbers
Trimmed_string_has_no_leading_whitespace对于没有业务逻辑的工具/帮助类代码,可以引用方法名,因为这类行为对业务人员没有意义:
Sum_of_two_numbers
Trimmed_string_has_no_leading_whitespaceLanguage-Specific Adaptations
不同语言的适配方案
Go
Go
Test functions MUST start with (language requirement). Append the descriptive name:
Testgo
func TestDelivery_with_a_past_date_is_invalid(t *testing.T) { ... }Subtests via have full naming freedom:
t.Rungo
func TestDelivery(t *testing.T) {
t.Run("with a past date is invalid", func(t *testing.T) { ... })
t.Run("with a future date is valid", func(t *testing.T) { ... })
}Table-driven tests — use descriptive fields, not method signatures:
namego
tests := []struct {
name string
// ...
}{
{"delivery with a past date is invalid", ...},
{"delivery for tomorrow is valid", ...},
}测试函数必须以开头(语言要求),后面追加描述性名称:
Testgo
func TestDelivery_with_a_past_date_is_invalid(t *testing.T) { ... }通过实现的子测试可以自由命名:
t.Rungo
func TestDelivery(t *testing.T) {
t.Run("with a past date is invalid", func(t *testing.T) { ... })
t.Run("with a future date is valid", func(t *testing.T) { ... })
}表驱动测试 —— 使用描述性的字段,不要用方法签名:
namego
tests := []struct {
name string
// ...
}{
{"delivery with a past date is invalid", ...},
{"delivery for tomorrow is valid", ...},
}Python (pytest)
Python (pytest)
Functions must start with . Append the descriptive name in snake_case:
test_python
def test_delivery_with_a_past_date_is_invalid():
...
def test_new_customer_starts_in_pending_state():
...函数必须以开头,后面追加snake_case格式的描述性名称:
test_python
def test_delivery_with_a_past_date_is_invalid():
...
def test_new_customer_starts_in_pending_state():
...Java / Kotlin (JUnit)
Java / Kotlin (JUnit)
@Testjava
@Test
void Delivery_with_a_past_date_is_invalid() { ... }
@Test
void New_customer_starts_in_pending_state() { ... }Kotlin supports backtick-quoted names for natural language:
kotlin
@Test
fun `delivery with a past date is invalid`() { ... }@Testjava
@Test
void Delivery_with_a_past_date_is_invalid() { ... }
@Test
void New_customer_starts_in_pending_state() { ... }Kotlin支持反引号包裹的名称,可以实现自然语言命名:
kotlin
@Test
fun `delivery with a past date is invalid`() { ... }JavaScript / TypeScript (Jest, Vitest, Mocha)
JavaScript / TypeScript (Jest, Vitest, Mocha)
String-based names — use natural language directly, no underscores needed:
javascript
it("delivery with a past date is invalid", () => { ... });
test("new customer starts in pending state", () => { ... });
describe("delivery validation", () => {
it("rejects past dates", () => { ... });
it("accepts future dates", () => { ... });
});基于字符串的命名 —— 直接使用自然语言,不需要下划线:
javascript
it("delivery with a past date is invalid", () => { ... });
test("new customer starts in pending state", () => { ... });
describe("delivery validation", () => {
it("rejects past dates", () => { ... });
it("accepts future dates", () => { ... });
});C# / .NET
C# / .NET
[Fact][Test]csharp
[Fact]
public void Delivery_with_a_past_date_is_invalid() { ... }使用或属性,命名完全自由,使用下划线分隔即可:
[Fact][Test]csharp
[Fact]
public void Delivery_with_a_past_date_is_invalid() { ... }Rust
Rust
#[test]rust
#[test]
fn delivery_with_a_past_date_is_invalid() { ... }使用属性,遵循标准snake_case命名规范:
#[test]rust
#[test]
fn delivery_with_a_past_date_is_invalid() { ... }Test Class / File Naming
测试类/文件命名
Use or as an entry point, not a boundary. The unit in unit testing is a unit of behavior, not a class — it can span multiple classes.
[ClassName]Tests[feature]_test使用或作为入口命名,而非边界限制。单元测试中的单元指的是行为单元,不是类单元,可以横跨多个类。
[ClassName]Tests[feature]_testWhat to Test — Behavior, Not Implementation
测试范围:行为,而非实现
- Test observable behavior: outputs, state changes, side effects visible to clients
- Never test implementation details: internal collaborations, private methods, exact SQL queries, specific method call sequences
- Renaming an internal method should never break a test
- If a test fails during a legit refactoring, the test is coupled to implementation
- 测试可观测的行为:输出、状态变更、对客户端可见的副作用
- 永远不要测试实现细节:内部协作逻辑、私有方法、具体SQL查询、特定方法调用顺序
- 重命名内部方法不应该导致测试失败
- 如果合理的重构导致测试失败,说明该测试与实现逻辑耦合度过高
Testing Styles (in order of preference)
测试风格(按优先度排序)
- Output-based — feed input, verify output. Best false-positive resistance. Use for pure functions and domain logic.
- State-based — perform operation, verify resulting state via public API. Good when output verification isn't possible.
- Communication-based (mocks) — verify interactions with dependencies. Use ONLY for uncontrolled external dependencies (SMTP, message bus, third-party APIs). Never mock domain objects or stable dependencies.
For deeper guidance on testing styles, value proposition, and pragmatic testing strategies, see testing-philosophy.md.
For common anti-patterns to avoid, see anti-patterns.md.
- 基于输出 —— 输入参数,验证输出。抗误报能力最强,适用于纯函数和领域逻辑
- 基于状态 —— 执行操作后,通过公共API验证最终状态。适合无法验证输出的场景
- 基于通信(mocks) —— 验证与依赖的交互。仅适用于不受控的外部依赖(SMTP、消息总线、第三方API),永远不要mock领域对象或稳定的依赖
如需更深入的测试风格指导、价值主张和务实测试策略,请查看testing-philosophy.md。
如需了解需要避免的常见反模式,请查看anti-patterns.md。