# When to use `t.plan()`Translations: [Espaรฑol](https://github.com/avajs/ava-docs/blob/main/es_ES/docs/recipes/when-to-use-plan.md), [Franรงais](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs/recipes/when-to-use-plan.md), [Italiano](https://github.com/avajs/ava-docs/blob/main/it_IT/docs/recipes/when-to-use-plan.md), [ๆฅๆฌ่ช](https://github.com/avajs/ava-docs/blob/main/ja_JP/docs/recipes/when-to-use-plan.md), [Portuguรชs](https://github.com/avajs/ava-docs/blob/main/pt_BR/docs/recipes/when-to-use-plan.md), [ะ ัััะบะธะน](https://github.com/avajs/ava-docs/blob/main/ru_RU/docs/recipes/when-to-use-plan.md), [็ฎไฝไธญๆ](https://github.com/avajs/ava-docs/blob/main/zh_CN/docs/recipes/when-to-use-plan.md)One major difference between AVA and [`tap`](https://github.com/tapjs/node-tap)/[`tape`](https://github.com/substack/tape) is the behavior of `t.plan()`. In AVA, `t.plan()` is only used to assert that the expected number of assertions are called; it does not auto-end the test.## Poor uses of `t.plan()`Many users transitioning from `tap`/`tape` are accustomed to using `t.plan()` prolifically in every test. However, in AVA, we don't consider that to be a "best practice". Instead, we believe `t.plan()` should only be used in situations where it provides some value.### Sync tests with no branching`t.plan()` is unnecessary in most sync tests.```jstest('simple sums', t => {// BAD: there is no branching here - t.plan() is pointlesst.plan(2);t.is(1 + 1, 2);t.is(2 + 2, 4);});````t.plan()` does not provide any value here, and creates an extra chore if you ever decide to add or remove assertions.### Promises that are expected to resolve```jstest('gives foo', t => {t.plan(1);return somePromise().then(result => {t.is(result, 'foo');});});```At a glance, this tests appears to make good use of `t.plan()` since an async promise handler is involved. However there are several problems with the test:1. `t.plan()` is presumably used here to protect against the possibility that `somePromise()` might be rejected; But returning a rejected promise would fail the test anyways.2. It would be better to take advantage of `async`/`await`:```jstest('gives foo', async t => {t.is(await somePromise(), 'foo');});```### Promises with a `.catch()` block```jstest('rejects with foo', t => {t.plan(2);return shouldRejectWithFoo().catch(reason => {t.is(reason.message, 'Hello');t.is(reason.foo, 'bar');});});```Here, the use of `t.plan()` seeks to ensure that the code inside the `catch` block is executed.Instead, you should take advantage of `t.throwsAsync` and `async`/`await`, as this leads to flatter code that is easier to reason about:```jstest('rejects with foo', async t => {const reason = await t.throwsAsync(shouldRejectWithFoo());t.is(reason.message, 'Hello');t.is(reason.foo, 'bar');});```### Ensuring a catch statement happens```jstest('throws', t => {t.plan(2);try {shouldThrow();} catch (err) {t.is(err.message, 'Hello');t.is(err.foo, 'bar');}});```As stated in the previous example, using the `t.throws()` assertion with `async`/`await` is a better choice.## Good uses of `t.plan()``t.plan()` provides value in the following cases.### Tests with branching statementsIn most cases, it's a bad idea to use any complex branching inside your tests. A notable exception is for tests that are auto-generated (perhaps from a JSON document). Below `t.plan()` is used to ensure the correctness of the JSON input:```jsimport fs from 'node:fs';import path from 'node:path';const testData = JSON.parse(fs.readFileSync(new URL('./fixtures/test-definitions.json', import.meta.url)));for (const testDefinition of testData) {test('foo or bar', t => {const result = functionUnderTest(testDefinition.input);// testDefinition should have an expectation for `foo` or `bar` but not botht.plan(1);if (testDefinition.foo) {t.is(result.foo, testDefinition.foo);}if (testDefinition.bar) {t.is(result.bar, testDefinition.foo);}});}```## Conclusion`t.plan()` has plenty of valid uses, but it should not be used indiscriminately. A good rule of thumb is to use it any time your *test* does not have straightforward, easily reasoned about, code flow. Tests with assertions inside callbacks, `if`/`then` statements, `for`/`while` loops, and (in some cases) `try`/`catch` blocks, are all good candidates for `t.plan()`.