Skip to content

Fail tests that finish with pending assertions #1335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 5, 2017

Conversation

novemberborn
Copy link
Member

t.throws() and t.notThrows() can be called with an observable or promise. This commit forces users to wait for the assertion to complete before finishing the test. Usually this means the test has to be written like:

test(async t => {
  await t.throws(Promise.reject(new Error()))
})

Or for callback tests:

test.cb(t => {
  t.throws(Promise.reject(new Error())).then(t.end)
})

This simplifies internal logic and helps discourage code like in #1327. Anecdotally users are surprised by the previous behavior where a synchronous test worked with an asynchronous assertion (#1327 (comment)).

Fixes #1327.

`t.throws()` and `t.notThrows()` can be called with an observable or
promise. This commit forces users to wait for the assertion to complete
before finishing the test. Usually this means the test has to be written
like:

```js
test(async t => {
  await t.throws(Promise.reject(new Error()))
})
```

Or for callback tests:

```js
test.cb(t => {
  t.throws(Promise.reject(new Error())).then(t.end)
})
```

This simplifies internal logic and helps discourage code like in #1327.
Anecdotally users are surprised by the previous behavior where a
synchronous test worked with an asynchronous assertion
(#1327 (comment)).

Fixes #1327.
@sindresorhus
Copy link
Member

Can we detect Promise/Observable being passed to t.throws/t.notThrows when using sync test test(t => {}); and disallow it?

@novemberborn
Copy link
Member Author

No, since we don't know a test is synchronous until the function returns.

@sindresorhus
Copy link
Member

But can't we throw when the test returns then? We could have t.throws add a flag to the test when used with a promise, then right after the test implementation is run, in the same tick, check the flag. If a test finishes in the same tick and doesn't return a promise, it's synchronous. This is not essential for this PR though. I just assume it will be a common issue.

@sindresorhus
Copy link
Member

This is actually already caught by another check (nice!), but would be useful to have a better message for it.

import test from 'ava';

const foo = () => Promise.resolve('foo');

test(t => {
	t.throws(foo());
});
  1 failed

  [anonymous]

  Test finished, but an assertion is still pending

@sindresorhus sindresorhus merged commit affbb45 into master Apr 5, 2017
@sindresorhus sindresorhus deleted the fail-if-test-ends-with-pending-assertions branch April 5, 2017 15:07
@sindresorhus
Copy link
Member

Yay. I'm very happy about this change.

@novemberborn
Copy link
Member Author

This is actually already caught by another check (nice!), but would be useful to have a better message for it.

Right. throws and notThrows are also the only assertions that can be pending. Are you saying we should track which assertion was used and base the error message on that?

@sindresorhus
Copy link
Member

@novemberborn We could if it's not too hard. I would also be good with just failing the test saying something like:

We detected t.throws or t.notThrows being used with a promise/observable, but the test is synchronous. Either use an async function or return a promise.

That's at least clearer than:

Test finished, but an assertion is still pending

@novemberborn
Copy link
Member Author

It's not too hard.

We detected t.throws or t.notThrows being used with a promise/observable, but the test is synchronous. Either use an async function or return a promise.

Unfortunately even if a test is asynchronous, that doesn't mean it's waiting for the assertion to complete.

@joflashstudios
Copy link

This change broke our test helpers - we were using t.notThrows to declare mocked functions that must be called. Now, instead of getting a specific error message, we get a generic "Test finished, but an assertion is still pending" instead of the error message passed into t.notThrows(promise, ), or the rejection itself.

@novemberborn
Copy link
Member Author

@joflashstudios for a generic solution you could await all t.notThrows() calls.

@joflashstudios
Copy link

@novemberborn The problem is that the t.notThrows() calls need to be non-blocking, because they happen before the promise gets resolved.

@novemberborn
Copy link
Member Author

You could keep a reference to the returned promises, and then at the end of the test implementation do return Promise.all(arrayOfReferences). You could even wrap that up in a function so you don't need to change your test code, like:

test('something', interceptNotThrows(t => {
  t.notThrows(somethingAsync())
}))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants