|
| 1 | +___ |
| 2 | +**备注** |
| 3 | + |
| 4 | +这是[when-to-use-plan.md](https://github.com/sindresorhus/ava/blob/master/docs/recipes/when-to-use-plan.md)的简体中文翻译。这个[链接](https://github.com/sindresorhus/ava/compare/master...zhaozhiming:master)用来查看本翻译与AVA的master分支是否有差别(如果你没有看到`when-to-use-plan.md`发生变化,那就意味着这份翻译文档是最新的)。 |
| 5 | +___ |
| 6 | + |
| 7 | +# 什么时候使用`t.plan()` |
| 8 | + |
| 9 | +翻译: [Español](https://github.com/sindresorhus/ava-docs/blob/master/es_ES/docs/recipes/when-to-use-plan.md), [Français](https://github.com/sindresorhus/ava-docs/blob/master/fr_FR/docs/recipes/when-to-use-plan.md), [日本語](https://github.com/sindresorhus/ava-docs/blob/master/ja_JP/docs/recipes/when-to-use-plan.md), [Português](https://github.com/sindresorhus/ava-docs/blob/master/pt_BR/docs/recipes/when-to-use-plan.md), [Русский](https://github.com/sindresorhus/ava-docs/blob/master/ru_RU/docs/recipes/when-to-use-plan.md), [简体中文](https://github.com/sindresorhus/ava-docs/blob/master/zh_CN/docs/recipes/when-to-use-plan.md) |
| 10 | + |
| 11 | +AVA和[`tap`](https://github.com/tapjs/node-tap)/[`tape`](https://github.com/substack/tape)的一个主要不同是`t.plan()`的行为。在AVA中`t.plan()`只是用来断言期望的断言数是否正确,并不会自动结束测试。 |
| 12 | + |
| 13 | +## `t.plan()`不好的用法 |
| 14 | + |
| 15 | +很多从`tap/tape`过渡过来的用户习惯在每个测试里大量使用`t.plan()`,尽管如此,在AVA里我们不认为这是一个“最佳实践”,我们反而认为`t.plan()`只有在少数情况才能提供其价值。 |
| 16 | + |
| 17 | +### 没有分支的同步测试 |
| 18 | + |
| 19 | +在大部分同步测试中`t.plan()`是没有必要的。 |
| 20 | + |
| 21 | +```js |
| 22 | +test(t => { |
| 23 | + // 不好:这里没有分支,t.plan是无意义的 |
| 24 | + t.plan(2); |
| 25 | + |
| 26 | + t.is(1 + 1, 2); |
| 27 | + t.is(2 + 2, 4); |
| 28 | +}); |
| 29 | +``` |
| 30 | + |
| 31 | +`t.plan()`在这里并没有提供任何价值,并且如果你决定添加或删除断言时会带来额外的工作。 |
| 32 | + |
| 33 | +### Promises期望得到resolve |
| 34 | + |
| 35 | +```js |
| 36 | +test(t => { |
| 37 | + t.plan(1); |
| 38 | + |
| 39 | + return somePromise().then(result => { |
| 40 | + t.is(result, 'foo'); |
| 41 | + }); |
| 42 | +}); |
| 43 | +``` |
| 44 | + |
| 45 | +简单看一下,这个测试充分利用了`t.plan()`的优势,因为这里面涉及到了异步promise处理。尽管如此在这个测试里还有一些问题: |
| 46 | + |
| 47 | +1. `t.plan()`用在这里大概是为了防止`somePromise()`可能被reject的情况,但返回一个reject的promise测试始终会失败。 |
| 48 | + |
| 49 | +2. 使用`async`/`await`可能会更好。 |
| 50 | + |
| 51 | +```js |
| 52 | +test(async t => { |
| 53 | + t.is(await somePromise(), 'foo'); |
| 54 | +}); |
| 55 | +``` |
| 56 | + |
| 57 | +## `t.plan()`好的用法 |
| 58 | + |
| 59 | +`t.plan()`有很多可接受的用法。 |
| 60 | + |
| 61 | +### Promise带上一个`.catch()`块 |
| 62 | + |
| 63 | +```js |
| 64 | +test(t => { |
| 65 | + t.plan(2); |
| 66 | + |
| 67 | + return shouldRejectWithFoo().catch(reason => { |
| 68 | + t.is(reason.message, 'Hello') // 如果你关心的是message的话使用t.throws()更好 |
| 69 | + t.is(reason.foo, 'bar'); |
| 70 | + }); |
| 71 | +}); |
| 72 | +``` |
| 73 | + |
| 74 | +在这里,`t.plan()`用来确保`catch`块中的代码被执行,在大部分情况下,你更应该使用`t.throws()`断言,但这是一个可以接受的用法,因为`t.throws()`只允许你断言错误的`message`属性。 |
| 75 | + |
| 76 | +### 确保一个catch语句发生 |
| 77 | + |
| 78 | +```js |
| 79 | +test(t => { |
| 80 | + t.plan(2); |
| 81 | + |
| 82 | + try { |
| 83 | + shouldThrow(); |
| 84 | + } catch (err) { |
| 85 | + t.is(err.message, 'Hello') // 如果你关心的是message的话使用t.throws()更好 |
| 86 | + t.is(err.foo, 'bar'); |
| 87 | + } |
| 88 | +}); |
| 89 | +``` |
| 90 | + |
| 91 | +像上面这种`try`/`catch`的情况,使用`t.throws()`一般是个更好的选择,但它只允许你断言错误的`message`属性。 |
| 92 | + |
| 93 | +### 确保多个callback被真正调用 |
| 94 | + |
| 95 | +```js |
| 96 | +test.cb(t => { |
| 97 | + t.plan(2); |
| 98 | + |
| 99 | + const callbackA = () => { |
| 100 | + t.pass(); |
| 101 | + t.end(); |
| 102 | + }; |
| 103 | + |
| 104 | + const callbackB = () => t.pass(); |
| 105 | + |
| 106 | + bThenA(callbackA, callbackB); |
| 107 | +}); |
| 108 | +``` |
| 109 | + |
| 110 | +上面确保`callbackB`先被调用(且只调用一次),紧接着调用`callbackA`,其他的组合不会满足计划。 |
| 111 | + |
| 112 | +### 测试带有分支语句 |
| 113 | + |
| 114 | +在大部分情况下,在测试中使用复杂的分支是一个坏主意,一个明显的例外是用来测试自动生成的东西(可能来自一个JSON文档)。下面的`t.plan()`用来确保JSON输入的正确性: |
| 115 | + |
| 116 | +```js |
| 117 | +const testData = require('./fixtures/test-definitions.json'); |
| 118 | + |
| 119 | +testData.forEach(testDefinition => { |
| 120 | + test(t => { |
| 121 | + const result = functionUnderTest(testDefinition.input); |
| 122 | + |
| 123 | + // testDefinition应该有一个`foo`或`bar`的预期而不是同时满足它们 |
| 124 | + t.plan(1); |
| 125 | + |
| 126 | + if (testDefinition.foo) { |
| 127 | + t.is(result.foo, testDefinition.foo); |
| 128 | + } |
| 129 | + |
| 130 | + if (testDefinition.bar) { |
| 131 | + t.is(result.bar, testDefinition.foo); |
| 132 | + } |
| 133 | + }); |
| 134 | +}); |
| 135 | +``` |
| 136 | + |
| 137 | +## 总结 |
| 138 | + |
| 139 | +`t.plan()`有很多有效的使用方法,但不应该被盲目使用。一个好的经验法则是你的*测试*没有简单的,容易推断的,代码流你可以使用它。测试在callback里带有断言,`if`/`then`语句,`for`/`while`循环,并且(在一些情况下)`try`/`catch`块,都可以考虑使用`t.plan()`。 |
| 140 | + |
0 commit comments