clean-tests
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClean Tests
整洁测试
T1: Insufficient Tests
T1: 测试覆盖不足
Test everything that could possibly break. Use coverage tools as a guide, not a goal.
python
undefined测试所有可能出现问题的场景。将覆盖率工具作为参考,而非目标。
python
undefinedBad - only tests happy path
不良示例 - 仅测试正常路径
def test_divide():
assert divide(10, 2) == 5
def test_divide():
assert divide(10, 2) == 5
Good - tests edge cases too
良好示例 - 同时测试边缘场景
def test_divide_normal():
assert divide(10, 2) == 5
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
def test_divide_negative():
assert divide(-10, 2) == -5
undefineddef test_divide_normal():
assert divide(10, 2) == 5
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
def test_divide_negative():
assert divide(-10, 2) == -5
undefinedT2: Use a Coverage Tool
T2: 使用覆盖率工具
Coverage tools report gaps in your testing strategy. Don't ignore them.
bash
undefined覆盖率工具可报告测试策略中的漏洞,不要忽略这些结果。
bash
undefinedRun with coverage
结合覆盖率工具运行测试
pytest --cov=myproject --cov-report=term-missing
pytest --cov=myproject --cov-report=term-missing
Aim for meaningful coverage, not 100%
追求有意义的覆盖率,而非100%覆盖率
undefinedundefinedT3: Don't Skip Trivial Tests
T3: 不要跳过简单测试
Trivial tests document behavior and catch regressions. They're worth more than their cost.
python
undefined简单测试可记录预期行为并捕获回归问题,其价值远高于编写成本。
python
undefinedWorth having - documents expected behavior
值得编写 - 记录预期行为
def test_user_default_role():
user = User(name="Alice")
assert user.role == "member"
undefineddef test_user_default_role():
user = User(name="Alice")
assert user.role == "member"
undefinedT4: An Ignored Test Is a Question About an Ambiguity
T4: 被忽略的测试意味着存在模糊性问题
Don't use to hide problems. Either fix the test or delete it.
@pytest.mark.skippython
undefined不要使用来掩盖问题。要么修复测试,要么删除它。
@pytest.mark.skippython
undefinedBad - hiding a problem
不良示例 - 掩盖问题
@pytest.mark.skip(reason="flaky, fix later")
def test_async_operation():
...
@pytest.mark.skip(reason="flaky, fix later")
def test_async_operation():
...
Good - either fix it or document why it's skipped
良好示例 - 要么修复,要么明确说明跳过原因
@pytest.mark.skip(reason="Requires Redis, see CONTRIBUTING.md for setup")
def test_cache_invalidation():
...
undefined@pytest.mark.skip(reason="Requires Redis, see CONTRIBUTING.md for setup")
def test_cache_invalidation():
...
undefinedT5: Test Boundary Conditions
T5: 测试边界条件
Bugs congregate at boundaries. Test them explicitly.
python
def test_pagination_boundaries():
items = list(range(100))
# First page
assert paginate(items, page=1, size=10) == items[0:10]
# Last page
assert paginate(items, page=10, size=10) == items[90:100]
# Beyond last page
assert paginate(items, page=11, size=10) == []
# Page zero (invalid)
with pytest.raises(ValueError):
paginate(items, page=0, size=10)
# Empty list
assert paginate([], page=1, size=10) == []漏洞往往集中在边界场景,需明确测试这些场景。
python
def test_pagination_boundaries():
items = list(range(100))
# 第一页
assert paginate(items, page=1, size=10) == items[0:10]
# 最后一页
assert paginate(items, page=10, size=10) == items[90:100]
# 超出最后一页
assert paginate(items, page=11, size=10) == []
# 第0页(无效)
with pytest.raises(ValueError):
paginate(items, page=0, size=10)
# 空列表
assert paginate([], page=1, size=10) == []T6: Exhaustively Test Near Bugs
T6: 针对已发现漏洞的相似场景进行全面测试
When you find a bug, write tests for all similar cases. Bugs cluster.
python
undefined当发现一个漏洞时,为所有相似场景编写测试。漏洞往往会集中出现。
python
undefinedFound bug: off-by-one in date calculation
发现漏洞:日期计算中的差一错误
Now test ALL date boundaries
现在测试所有日期边界场景
def test_month_boundaries():
assert last_day_of_month(2024, 1) == 31 # January
assert last_day_of_month(2024, 2) == 29 # Leap year February
assert last_day_of_month(2023, 2) == 28 # Non-leap February
assert last_day_of_month(2024, 4) == 30 # 30-day month
assert last_day_of_month(2024, 12) == 31 # December
undefineddef test_month_boundaries():
assert last_day_of_month(2024, 1) == 31 # 一月
assert last_day_of_month(2024, 2) == 29 # 闰年二月
assert last_day_of_month(2023, 2) == 28 # 非闰年二月
assert last_day_of_month(2024, 4) == 30 # 30天的月份
assert last_day_of_month(2024, 12) == 31 # 十二月
undefinedT7: Patterns of Failure Are Revealing
T7: 测试失败的模式具有参考价值
When tests fail, look for patterns. They often point to deeper issues.
python
undefined当测试失败时,寻找其中的模式,它们通常指向更深层次的问题。
python
undefinedIf all async tests fail intermittently,
如果所有异步测试都间歇性失败,
the problem isn't the tests—it's the async handling
问题不在测试本身——而是异步处理逻辑有问题
undefinedundefinedT8: Test Coverage Patterns Can Be Revealing
T8: 测试覆盖率模式具有参考价值
Look at which code paths are untested. Often they reveal design problems.
python
undefined查看哪些代码路径未被测试,它们通常会暴露设计问题。
python
undefinedIf you can't easily test a function, it probably does too much
如果某个函数难以测试,它可能承担了过多职责
Refactor for testability
需重构以提升可测试性
undefinedundefinedT9: Tests Should Be Fast
T9: 测试应快速执行
Slow tests don't get run. Keep unit tests under 100ms each.
python
undefined缓慢的测试不会被频繁运行。确保每个单元测试的执行时间在100ms以内。
python
undefinedBad - hits real database
不良示例 - 连接真实数据库
def test_user_creation():
db = connect_to_database() # Slow!
user = db.create_user("Alice")
assert user.name == "Alice"
def test_user_creation():
db = connect_to_database() # 速度慢!
user = db.create_user("Alice")
assert user.name == "Alice"
Good - uses mock or in-memory
良好示例 - 使用模拟或内存数据库
def test_user_creation():
db = InMemoryDatabase()
user = db.create_user("Alice")
assert user.name == "Alice"
undefineddef test_user_creation():
db = InMemoryDatabase()
user = db.create_user("Alice")
assert user.name == "Alice"
undefinedTest Organization
测试组织规范
F.I.R.S.T. Principles
F.I.R.S.T. 原则
- Fast: Tests should run quickly
- Independent: Tests shouldn't depend on each other
- Repeatable: Same result every time, any environment
- Self-Validating: Pass or fail, no manual inspection
- Timely: Written before or with the code, not after
- Fast:测试应快速运行
- Independent:测试之间不应存在依赖关系
- Repeatable:在任何环境下,每次运行都能得到相同结果
- Self-Validating:通过或失败,无需人工检查
- Timely:在代码编写之前或同时编写测试,而非之后
One Concept Per Test
每个测试仅验证一个概念
python
undefinedpython
undefinedBad - testing multiple things
不良示例 - 同时测试多个内容
def test_user():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
assert user.email == "alice@example.com"
assert user.is_valid()
user.activate()
assert user.is_active
def test_user():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
assert user.email == "alice@example.com"
assert user.is_valid()
user.activate()
assert user.is_active
Good - one concept each
良好示例 - 每个测试仅验证一个概念
def test_user_stores_name():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
def test_user_stores_email():
user = User("Alice", "alice@example.com")
assert user.email == "alice@example.com"
def test_new_user_is_valid():
user = User("Alice", "alice@example.com")
assert user.is_valid()
def test_user_can_be_activated():
user = User("Alice", "alice@example.com")
user.activate()
assert user.is_active
undefineddef test_user_stores_name():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
def test_user_stores_email():
user = User("Alice", "alice@example.com")
assert user.email == "alice@example.com"
def test_new_user_is_valid():
user = User("Alice", "alice@example.com")
assert user.is_valid()
def test_user_can_be_activated():
user = User("Alice", "alice@example.com")
user.activate()
assert user.is_active
undefinedQuick Reference
快速参考
| Rule | Principle |
|---|---|
| T1 | Test everything that could break |
| T2 | Use coverage tools |
| T3 | Don't skip trivial tests |
| T4 | Ignored test = ambiguity question |
| T5 | Test boundary conditions |
| T6 | Exhaustively test near bugs |
| T7 | Look for patterns in failures |
| T8 | Check coverage when debugging |
| T9 | Tests must be fast (<100ms) |
| 规则 | 原则 |
|---|---|
| T1 | 测试所有可能出现问题的场景 |
| T2 | 使用覆盖率工具 |
| T3 | 不要跳过简单测试 |
| T4 | 被忽略的测试 = 存在模糊性问题 |
| T5 | 测试边界条件 |
| T6 | 针对已发现漏洞的相似场景进行全面测试 |
| T7 | 寻找测试失败中的模式 |
| T8 | 调试时检查覆盖率 |
| T9 | 测试必须快速(<100ms) |