写单元测试是你从"会写代码"到"专业开发者"的分水岭。这篇文章用最少的理论带你直接上手。

为什么必须写测试

  • 重构有底气 — 有测试覆盖的代码,改完跑一次就知道有没有破坏现有功能
  • 文档即测试 — 测试描述了函数在各种输入下应该如何表现,比注释更可靠
  • 减少回归 Bug — 修一个 Bug 加一个测试,同样的错不会再犯第二次

AAA 模式(Arrange-Act-Assert)

def test_add_two_numbers():
    # Arrange(准备)
    a, b = 2, 3
    # Act(执行)
    result = add(a, b)
    # Assert(断言)
    assert result == 5

第一个真实测试

# user_service.py
def get_full_name(user):
    return f"{user.first_name} {user.last_name}"

# test_user_service.py
def test_get_full_name():
    user = type('User', (), {'first_name': '张', 'last_name': '三'})()
    assert get_full_name(user) == "张 三"

def test_get_full_name_empty_last():
    user = type('User', (), {'first_name': '李', 'last_name': ''})()
    assert get_full_name(user) == "李 "

Mock 和 Fixture

# Fixture: 共享的测试数据
@pytest.fixture
def sample_user():
    return User(id=1, name="张三", email="zhang@test.com")

def test_user_email(sample_user):
    assert sample_user.email == "zhang@test.com"

# Mock: 隔离外部依赖
@patch("requests.get")
def test_fetch_user(mock_get):
    mock_get.return_value.json.return_value = {"name": "张三"}
    result = fetch_user(1)
    assert result["name"] == "张三"

什么该测、什么不该测

  • 该测 — 业务逻辑、边界条件、错误路径、数据转换
  • 不该测 — 简单的 getter/setter、框架代码、第三方库的内部行为

起步建议

不用追求 100% 覆盖率——那会增加大量维护负担。先给核心业务逻辑写测试,看到覆盖率数字攀升的成就感会推着你继续写。