备注
这是 when-to-use-plan.md 的简体中文翻译。这个链接 用来查看本翻译与 AVA 的 master 分支是否有差别(如果你没有看到when-to-use-plan.md
发生变化,那就意味着这份翻译文档是最新的)。
翻译:Español, Français, Italiano, 日本語, Português, Русский, 简体中文
AVA 和 tap
/tape
的一个主要不同是t.plan()
的行为。在 AVA 中t.plan()
只是用来断言期望的断言数是否正确,并不会自动结束测试。
很多从tap/tape
过渡过来的用户习惯在每个测试里大量使用t.plan()
,尽管如此,在 AVA 里我们不认为这是一个“最佳实践”,我们反而认为t.plan()
只有在少数情况才能提供其价值。
在大部分同步测试中t.plan()
是没有必要的。
test(t => {
// 不好:这里没有分支,t.plan 是无意义的
t.plan(2);
t.is(1 + 1, 2);
t.is(2 + 2, 4);
});
t.plan()
在这里并没有提供任何价值,并且如果你决定添加或删除断言时会带来额外的工作。
test(t => {
t.plan(1);
return somePromise().then(result => {
t.is(result, 'foo');
});
});
简单看一下,这个测试充分利用了t.plan()
的优势,因为这里面涉及到了异步 promise 处理。尽管如此在这个测试里还有一些问题:
-
t.plan()
用在这里大概是为了防止somePromise()
可能被 reject 的情况,但返回一个 reject 的 promise 测试始终会失败。 -
使用
async
/await
可能会更好。
test(async t => {
t.is(await somePromise(), 'foo');
});
t.plan()
有很多可接受的用法。
test(t => {
t.plan(2);
return shouldRejectWithFoo().catch(reason => {
t.is(reason.message, 'Hello') // 如果你关心的是 message 的话使用 t.throws() 更好
t.is(reason.foo, 'bar');
});
});
在这里,t.plan()
用来确保catch
块中的代码被执行,在大部分情况下,你更应该使用t.throws()
断言,但这是一个可以接受的用法,因为t.throws()
只允许你断言错误的message
属性。
test(t => {
t.plan(2);
try {
shouldThrow();
} catch (err) {
t.is(err.message, 'Hello') // 如果你关心的是 message 的话使用 t.throws() 更好
t.is(err.foo, 'bar');
}
});
像上面这种try
/catch
的情况,使用t.throws()
一般是个更好的选择,但它只允许你断言错误的message
属性。
test.cb(t => {
t.plan(2);
const callbackA = () => {
t.pass();
t.end();
};
const callbackB = () => t.pass();
bThenA(callbackA, callbackB);
});
上面确保callbackB
先被调用(且只调用一次),紧接着调用callbackA
,其他的组合不会满足计划。
在大部分情况下,在测试中使用复杂的分支是一个坏主意,一个明显的例外是用来测试自动生成的东西(可能来自一个 JSON 文档)。下面的t.plan()
用来确保 JSON 输入的正确性:
const testData = require('./fixtures/test-definitions.json');
testData.forEach(testDefinition => {
test(t => {
const result = functionUnderTest(testDefinition.input);
// testDefinition 应该有一个`foo`或`bar`的预期而不是同时满足它们
t.plan(1);
if (testDefinition.foo) {
t.is(result.foo, testDefinition.foo);
}
if (testDefinition.bar) {
t.is(result.bar, testDefinition.foo);
}
});
});
t.plan()
有很多有效的使用方法,但不应该被盲目使用。一个好的经验法则是你的测试没有简单的,容易推断的,代码流的话你可以使用它。测试在 callback 里带有断言,if
/then
语句,for
/while
循环,并且(在一些情况下)try
/catch
块,都可以考虑使用t.plan()
。