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

测试工具实战

隔离配置测试中的全局状态

在 TT Lab 中继续学习

目标

验证错误的环境值、默认值、快照,以及机密字段暴露的回归。

为什么重要

先运行的测试修改了环境变量,导致后面的测试失败。把测试顺序固定下来虽然掩盖了问题,但实际应用中连配置拼写错误都会变成正常默认值的 bug 依然存在。必须分别确认输入隔离和严格解析。

步骤

  1. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_bool(value) 仅当忽略大小写后为 true 或 false 时,才返回对应的 bool。带有空白,或不是字符串时,抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

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

mkdir -p /root/work/test-settings-isolation-lab
test -e /root/work/test-settings-isolation-lab/service.py || cp /opt/fixtures/ten_labs/test-settings-isolation-lab/service.py /root/work/test-settings-isolation-lab/service.py
test -e /root/work/test-settings-isolation-lab/test_service.py || cp /opt/fixtures/ten_labs/test-settings-isolation-lab/test_service.py /root/work/test-settings-isolation-lab/test_service.py
cd /root/work/test-settings-isolation-lab
  1. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_port(value) 把只含 ASCII 数字的字符串转换为 int,若在 1–65535 范围内则返回。其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  2. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_timeout(value) 把字符串转换为 float,只返回大于 0 且不超过 30 的有限值。其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  3. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:required_token(env) 在 TOKEN 是字符串且 strip 之后不为空时,返回 strip 后的值。缺失或为空值时抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  4. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:load_settings(env) 返回这样的字典:service 取 env 的 SERVICE,缺失时为 'api';debug=parse_bool(DEBUG,缺失时为 'false');port=parse_port(PORT,缺失时为 '8000');timeout=parse_timeout(TIMEOUT,缺失时为 '5');token=required_token。空的 SERVICE 抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  5. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:public_settings(settings) 返回只含 service 和 debug 的新字典。不修改原始字典。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  6. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:snapshot(env) 返回 load_settings 的结果。调用之后即使修改 env,返回的配置也不会改变。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

  7. 在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:create_app(env) 会立即读取 snapshot,配置有误时以 ValueError 使应用创建失败。GET /info 只返回 public_settings。用不同 env 创建的应用之间不共享配置。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

参考

显式解析布尔值——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_bool(value) 仅当忽略大小写后为 true 或 false 时,才返回对应的 bool。带有空白,或不是字符串时,抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

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

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

bool('false') 的结果是 True。请直接比较允许的两个字符串。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

检查端口范围——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_port(value) 把只含 ASCII 数字的字符串转换为 int,若在 1–65535 范围内则返回。其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

整数转换成功,并不意味着它是有效的端口范围。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

让超时时间成为有限值——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:parse_timeout(value) 把字符串转换为 float,只返回大于 0 且不超过 30 的有限值。其余抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

NaN 在普通比较中的行为与预期不同,所以要检查 isfinite。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

拒绝缺失的必需机密——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:required_token(env) 在 TOKEN 是字符串且 strip 之后不为空时,返回 strip 后的值。缺失或为空值时抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

不要用示例默认值顶替缺失的必需机密。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

只对缺失的项应用默认值——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:load_settings(env) 返回这样的字典:service 取 env 的 SERVICE,缺失时为 'api';debug=parse_bool(DEBUG,缺失时为 'false');port=parse_port(PORT,缺失时为 '8000');timeout=parse_timeout(TIMEOUT,缺失时为 '5');token=required_token。空的 SERVICE 抛出 ValueError。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

get 的默认值与“值 or 默认值”在处理空字符串时并不相同。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

从公开配置中移除机密——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:public_settings(settings) 返回只含 service 和 debug 的新字典。不修改原始字典。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

与其对令牌值的一部分做掩码,不如采用根本不公开该字段的契约。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

把外部变更与配置分开——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:snapshot(env) 返回 load_settings 的结果。调用之后即使修改 env,返回的配置也不会改变。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

把应用启动时刻的配置,与之后可能改变的输入字典分开。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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

确认启动失败与公开响应——测试

在 /root/work/test-settings-isolation-lab/test_service.py 中测试所提供 service.py 的下列公开契约:create_app(env) 会立即读取 snapshot,配置有误时以 ValueError 使应用创建失败。GET /info 只返回 public_settings。用不同 env 创建的应用之间不共享配置。在正确实现上应当通过,而在违反该契约的实现上,必须由测试本体的实际失败将其检出。保留前面步骤的测试,并添加 test_ 函数。

在创建时就进行验证,避免服务器启动之后,配置错误要到第一个请求时才暴露。不要修改实现文件。用 pytest.raises 确认预期的异常,对正常结果则断言具体的预期值。

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