Elixir 从零 · 第 24 课(展开目录)
01 · 先让代码说话02 · 先认六种值03 · 把值装起来04 · 从旧 list 生成新 list05 · 让形状对上06 · 用 case 做选择07 · 把文字安全变成整数08 · 拆开 &1 和管道09 · 把代码放进项目10 · 真假不只靠 true11 · 走进嵌套数据12 · 一字不一定一字节13 · 让函数按形状接活14 · 把一列数收成一个值15 · 给不同问题选不同路16 · 给 map 一张名片17 · 让项目自己验答案18 · 让成功和失败长得一样19 · 读写文件先管好路20 · 只算眼前需要的21 · 同一句话,不同做法22 · 先约好模块会做什么23 · 把数据形状写在门口24 · 测试不只看一条好路25 · 把项目磨成一个命令26 · 定下小项目边界27 · 把真实文字洗干净28 · 只开三扇门29 · 把约定钉在代码旁30 · 打包,也留下脚印31 · 照任务书交卷
ELIXIR · 进阶 · LESSON 2440 分钟
测试不只看一条好路
用 `describe`、`setup` 和边界用例组织可读的 ExUnit 测试。
01 · 先看完整例子
让边界写进测试名
例子假设项目已有 Discount.price/2;重点看测试边界怎样表达。
Elixir
defmodule DiscountTest do
use ExUnit.Case, async: true
describe "price/2" do
test "满 10 件打九折" do
assert Discount.price(100, 10) == 90.0
end
# 9 件仍按原价
test "未满 10 件不打折" do
assert Discount.price(100, 9) == 100
end
end
end先对照结果
- 实现符合规则时显示
2 tests, 0 failures。
02 · 回头拆代码
从第一行往下读
- 两个测试只差边界两侧的数量,容易读出业务规则。
- 测试名说“什么情况下,结果怎样”,不说函数内部怎样写。
- 失败后先读期望值和实际值,再定位实现。
03 · 认清新符号
符号不是暗号
describe把同一功能的测试放在一组。
setup每个测试前准备共享数据。
assert_raise要求一段代码抛出指定异常。
04 · 代码里的概念
把名字和意思对上
边界用例
规则刚好发生变化的位置,如满 10 件、恰好 0 元。
测试上下文
setup 交给每个测试的一份准备数据。
05 · 再说清楚
这些写法为什么有用
好测试像路标:名字说明行为,输入够小,失败时能看出哪里偏了。
先测正常例子,再补边界和错误输入。describe 按功能分组,setup 只准备真正共用的数据。
测试不要复制实现。若正文和测试用同一套算法,两个地方可能一起错。
06 · 自己改一次
合上答案,动手
增加恰好 0 件的测试,期待原价。
练习起点
test "零件商品不打折" do
assert Discount.price(80, ____) == ____
end目标结果: 测试期待 Discount.price(80, 0) == 80。
卡住了,再看提示
数量为 0,价格仍写整数 80。
运行过以后,再看一种答案
一种答案
test "零件商品不打折" do
# 零也是需要明确保存的边界
assert Discount.price(80, 0) == 80
end想一想:每个测试都需要 `setup` 吗?
不需要。只有多条测试确实共享准备步骤时才用;简单输入直接写在测试里更清楚。
带走
记住三句
- 1
测试名应说明行为。
- 2
正常、边界、失败三类输入都要考虑。
- 3
共享准备要少而明确。
本课结束代码也跑过一次,就把这一课收好。