后端单测怎么写:把外部依赖挡在边界外

米阳 2025-8-14 6 8/14

很多人写单测半途而废,不是因为不会写断言,而是因为测试一跑就要连数据库、发 HTTP、等定时任务,慢且不稳定。关键是把外部依赖挡在边界之外。

一、先测纯逻辑,别急着测全流程

把一个方法里「算」的部分和「取数」的部分拆开:

# 不好:方法里直接查库、直接算、直接发消息
def create_order(user_id, items): ...

# 好:计算逻辑独立出来,可单独测
def calc_amount(items, coupon):
    ...

def create_order(user_id, items):
    amount = calc_amount(items, load_coupon(user_id))
    save_order(...)

calc_amount 这种纯函数是单测性价比最高的目标:不用 mock、跑得飞快、边界条件一目了然。

二、依赖用接口隔离

数据库、缓存、第三方客户端都通过接口注入,测试时替换成内存实现或 mock:

class Repo(Protocol):
    def get(self, key: str) -> dict | None: ...

def service(repo: Repo, key: str):
    obj = repo.get(key)
    ...

这样测试只验证「拿到 X 时该做 Y」,不需要真的启一个数据库。

三、测试该断言什么

优先断言行为结果(返回值、状态变化、是否调用了某个写操作),而不是断言内部实现细节(调用了哪个私有方法、调用几次)。后者一重构测试就全红,维护成本极高。

一个用例覆盖一个场景,命名说清条件与预期:

test_余额不足时_应拒绝下单并保留库存
test_重复请求_应返回同一订单号

四、边界清单

写用例时按这张表过一遍,覆盖度通常就够了:

  • 空值 / 空集合 / None
  • 边界值:0、负数、最大值、刚好等于阈值
  • 重复与并发:同一请求两次、同一资源竞争
  • 外部失败:超时、返回错误、返回空
  • 状态机:非法状态跳转是否被拒

五、哪些不该进单测

跨服务流程、真实数据库约束、消息队列投递,这些交给集成测试,用容器起一套真实依赖,数量少但跑得稳。单测追求的是毫秒级反馈,一旦某个用例要等 2 秒,开发者就不会愿意频繁跑了。

小结

把计算逻辑抽成纯函数、把 I/O 挡在接口后面、断言行为而非实现。做到这三点,单测就从负担变成了改代码时的底气。

- THE END -

米阳

8月14日09:50

最后修改:2025年8月14日
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论