TT Lab
开始
学习 学习路径 课程

测试工具实战

让惰性导出行为可被观察

在 TT Lab 中继续学习

目标

验证行的消费次数、换行转义、公开字段和响应格式。

为什么重要

用小列表测试过的导出代码看上去一切正常,却在把大输入全部转成 list 时耗尽了内存。结果相同,并不意味着运行方式的契约也相同。必须把惰性消费变成肉眼可见的状态。

步骤

  1. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:validate_row(row) 在 row 是 dict、id 是除 bool 之外的正 int、name 是非空 str 时,返回 row。其余抛出 ValueError。允许存在额外的内部字段。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab
  1. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:project(row) 在 validate_row 之后,返回只含 id 和 name 的新 dict。原始的内部字段保持不变。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  2. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:encode_line(row) 把 project 的结果用 ensure_ascii=False、separators=(',',':')、sort_keys=True 编码为 JSON,并在末尾追加一个 '\n',返回 str。name 中的换行必须是 JSON 转义。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  3. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:validate_max(value) 只在 bool 之外的 int 处于 1–1000 范围内时原样返回,其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  4. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:take_rows(rows, maximum) 是一个 iterator,用 islice 等方式最多惰性地返回 maximum 个。调用时会验证 maximum,每调用一次 next,只消费一次输入。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  5. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:json_lines(rows, maximum=100) 对从 take_rows 得到的每一行 yield 出 encode_line。不返回把全部拼起来的字符串或列表。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  6. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:decode_lines(text) 对 splitlines 得到的每个非空行先 json.loads,再 validate_row,并以列表返回。空字符串返回 [],中间出现空行则抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  7. 在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:create_app(rows) 在 GET /export 上,把 json_lines(rows, 100) 作为 application/x-ndjson 的 StreamingResponse 返回。rows 是可再次遍历的列表。不能含有内部字段,并且必须保留每一行的内容和顺序。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

参考

验证行契约——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:validate_row(row) 在 row 是 dict、id 是除 bool 之外的正 int、name 是非空 str 时,返回 row。其余抛出 ValueError。允许存在额外的内部字段。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab

区分 bool 与数字,并把空名称当作错误处理。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/01-contract.sh 确认。

只生成公开行——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:project(row) 在 validate_row 之后,返回只含 id 和 name 的新 dict。原始的内部字段保持不变。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

导出路径也必须与普通 API 采用相同的公开字段策略。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/02-contract.sh 确认。

保留行边界地编码——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:encode_line(row) 把 project 的结果用 ensure_ascii=False、separators=(',',':')、sort_keys=True 编码为 JSON,并在末尾追加一个 '\n',返回 str。name 中的换行必须是 JSON 转义。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

如果用字符串拼接来构造 JSON,遇到引号和换行时格式就会被破坏。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/03-contract.sh 确认。

验证输出数量上限——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:validate_max(value) 只在 bool 之外的 int 处于 1–1000 范围内时原样返回,其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

要求调用方给出上限,避免不小心把无限输入一直读到底。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/04-contract.sh 确认。

只消费需要的行——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:take_rows(rows, maximum) 是一个 iterator,用 islice 等方式最多惰性地返回 maximum 个。调用时会验证 maximum,每调用一次 next,只消费一次输入。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

一旦转换成 list(rows),就无法处理无限输入和大容量输入了。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/05-contract.sh 确认。

惰性序列化行——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:json_lines(rows, maximum=100) 对从 take_rows 得到的每一行 yield 出 encode_line。不返回把全部拼起来的字符串或列表。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

让对象的选取与表示形式的转换,各自都保持为惰性阶段。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/06-contract.sh 确认。

重新验证下载的行——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:decode_lines(text) 对 splitlines 得到的每个非空行先 json.loads,再 validate_row,并以列表返回。空字符串返回 [],中间出现空行则抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

区分空文件与格式被破坏的空记录。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/07-contract.sh 确认。

完成 HTTP 下载——测试

在 /root/work/test-lazy-export-lab/test_service.py 中测试所提供 service.py 的下列公开契约:create_app(rows) 在 GET /export 上,把 json_lines(rows, 100) 作为 application/x-ndjson 的 StreamingResponse 返回。rows 是可再次遍历的列表。不能含有内部字段,并且必须保留每一行的内容和顺序。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

要同时配合生成器测试,确认没有只在 Content-Type 上写流式、内部却把全部内容收集起来。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

保存后用 bash /opt/lab/checks/test-lazy-export-lab/08-contract.sh 确认。