关于 test-namer
面向开发者的测试技能,指导使用通俗英语描述可观察行为,而非实现细节。它提供针对不同语言的命名建议,并强调以行为为中心的测试。
主要功能
- 01用通俗英语描述行为
- 02避免僵化命名模板
- 03适配各语言命名惯例
- 04优先测试可观察行为
- 05选择合适的测试风格
适用场景
- 01为领域行为测试命名
- 02编写 Go 或 Python 测试
- 03命名 JavaScript 测试用例
- 04检查耦合实现细节的测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mryll/ skills --skill test-namer安装后在 Claude Code 中输入/test-namer确认已加载。
附带文件references/anti-patterns.mdreferences/testing-philosophy.md
详细介绍
Test Namer
编写用通俗英语描述行为而非实现细节的测试。基于 Vladimir Khorikov 的测试原则。
命名指南
规则
- 不要采用死板的命名策略——切勿使用
[MethodUnderTest]_[Scenario]_[ExpectedResult]或类似模板 - 向熟悉问题领域的非程序员描述行为——领域专家应该能理解测试名称
- 在函数/方法名称中用下划线分隔单词(基于字符串的测试名称,例如 JS/TS 中则不需要)
- 不要在测试名称中包含 SUT 的方法名称——测试行为,而不是方法
- 陈述基本事实,而非愿望——写
is_invalid,不要写should_be_invalid - 使用基本英语语法——冠词(
a、the)能提升可读性 - 明确具体——
Delivery_with_a_past_date_is_invalid比Delivery_with_invalid_date_is_invalid更好
命名改进示例
从死板的约定开始,逐步改进:
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)
例外:工具代码
对于没有业务逻辑的工具/辅助代码,可以引用方法名称,因为其行为对业务人员没有意义:
Sum_of_two_numbers
Trimmed_string_has_no_leading_whitespace
特定语言的调整
Go
测试函数必须以 Test 开头(语言要求)。后接描述性名称:
func TestDelivery_with_a_past_date_is_invalid(t *testing.T) { ... }
通过 t.Run 编写的子测试,命名不受限制:
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) { ... })
}
表驱动测试——使用描述性的 name 字段,而不是方法签名:
tests := []struct {
name string
// ...
}{
{"delivery with a past date is invalid", ...},
{"delivery for tomorrow is valid", ...},
}
Python (pytest)
函数必须以 test_ 开头。后接 snake_case 格式的描述性名称:
def test_delivery_with_a_past_date_is_invalid():
...
def test_new_customer_starts_in_pending_state():
...
Java / Kotlin (JUnit)
@Test 注解负责测试发现。方法名称可以完全自由地描述:
@Test
void Delivery_with_a_past_date_is_invalid() { ... }
@Test
void New_customer_starts_in_pending_state() { ... }
Kotlin 支持使用反引号括起自然语言名称:
@Test
fun `delivery with a past date is invalid`() { ... }
JavaScript / TypeScript (Jest, Vitest, Mocha)
基于字符串的名称——直接使用自然语言,无需下划线:
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
使用 [Fact] 或 [Test] 属性。下划线用法不受限制:
[Fact]
public void Delivery_with_a_past_date_is_invalid() { ... }
Rust
使用 #[test] 属性。采用标准的 snake_case 标识符:
#[test]
fn delivery_with_a_past_date_is_invalid() { ... }
测试类/文件命名
使用 [ClassName]Tests 或 [feature]_test 作为入口点,而非边界。单元测试中的单元指一个行为单元,而非一个类——它可以跨越多个类。
测试什么——行为,而非实现
- 测试可观察的行为:输出、状态变化、客户端可见的副作用
- 绝不测试实现细节:内部协作、私有方法、精确的 SQL 查询、特定的方法调用顺序
- 重命名内部方法绝不应该导致测试失败
- 如果测试在合理重构期间失败,说明测试与实现耦合
测试风格(按优先顺序)
- 基于输出——输入数据,验证输出。最能抵御误报。适用于纯函数和领域逻辑。
- 基于状态——执行操作,通过公共 API 验证最终状态。无法验证输出时很有用。
- 基于通信(模拟)——验证与依赖项的交互。仅用于不受控的外部依赖(SMTP、消息总线、第三方 API)。绝不模拟领域对象或稳定的依赖项。
有关测试风格、价值主张和务实测试策略的深入指南,请参阅 testing-philosophy.md。
有关应避免的常见反模式,请参阅 anti-patterns.md。
评论
支持 Markdown