afterEach and beforeEach not running?
kentcdodds opened this issue · comments
I've reproduced this bug in this repo. From the README there:
The
test
directory has abug.js
and aworkaround.js
.If you run:
npm install
and thennpm t
you'll get:~/Developer/ava-beforeEach-afterEach-bug (master) ⛄ $ npm run test -s ✔ workaround › first log ✔ workaround › second log ✔ bug › first log ✖ bug › second log t.true(console.log.calledOnce) | false 1 test failed 1. second log AssertionError: false === true Test.fn (test/bug.js:16:3)
For some reason, it appears that perhaps the cleanup function is not run between the two test runs.
It's using the latest version of AVA. Specifically, I spotted this here. Let me know if you need more info!
What's the callCount
of console.log
?
Think I found the issue. npm test -s
does not run your tests in serial, which is why it doesn't work. Update your npm script to "test": "ava -s"
and try again.
You can also make use of the context object to store your original log.
import test from 'ava';
import sinon from 'sinon';
test.beforeEach(t => {
t.context.log = console.log;
console.log = sinon.spy();
});
test.afterEach(t => {
console.log = t.context.log;
});
test('first', t => {
console.log('first');
t.true(console.log.calledOnce);
});
test('second', t => {
console.log('second');
t.true(console.log.calledOnce);
});
Yes, from the AVA readme:
When using
npm test
, you can pass positional arguments directlynpm test test2.js
, but flags needs to be passed likenpm test -- --verbose
.
So it has to be npm test -- -s
(Really wish npm test would just pass them through... Anyone wanna open a npm ticket about that?)
@sindresorhus Cool, didn't know it was possible. Thanks :).
Actually I'm not trying to pass the -s
flag to AVA. I'm using it to suppress npm run output. I want aware that using beforeEach and afterEach require that tests run serially. I guess that makes sense, but I can't think of a use case where including those would be useful when running in parallel... Is there a better way to do what I'm trying to do?
@kentcdodds They don't require that tests run serially. It's because console.log
is global, so shared between the two tests. This means that console.log
is referring to the same spy. So in this situation, it's necessary to run them serially. If you are able (which is not the case here) to use the context
object, you don't have to run them serially because it's a totally new object.
import test from 'ava';
import sinon from 'sinon';
test.beforeEach(t => {
t.context.log = sinon.spy();
});
test.afterEach(t => {
t.context.log.restore();
});
test('first', t => {
t.context.log('first');
t.true(t.context.log.calledOnce);
});
test('second', t => {
t.context.log('second');
t.true(t.context.log.calledOnce);
});
@SamVerschueren On a side note, the above would be great as a recipe. Hint hint ;)
That won't work for my actual use-case. And it doesn't make sense to me that the tests are using the same spy because the spy should be reset before each test.
@kentcdodds Can you elaborate on your use-case? They're not using the same spy. The hook is run before each test and with a unique t.context
for each test.
And it doesn't make sense to me that the tests are using the same spy because the spy should be reset before each test.
They are, but AVA is just too fast :).
Look at the execution flow
beforeEach
beforeEach
first
second
afterEach
Note: Not sure though why the second
afterEach
isn't called. Bug? @sindresorhus
As you see, the console.log
will be the same for first
and second
, being the one created in the second call to beforeEach
. So yes, they are shared :).
@sindresorhus He was pointing to the use case without the context object
On a side note, the above would be great as a recipe. Hint hint ;)
Will do my best to not forget. Should create a repository to keep track of my todos :).
Shared variable
import test from 'ava';
let i = 0;
test.beforeEach(t => {
i++;
});
test.afterEach(t => {
i--;
});
test('first', t => {
console.log(i);
});
test('second', t => {
console.log(i);
});
Output:
2
2
Context variable
import test from 'ava';
test.beforeEach(t => {
t.context.i = (t.context.i || 0) + 1;
});
test.afterEach(t => {
t.context.i--;
});
test('first', t => {
console.log(t.context.i);
});
test('second', t => {
console.log(t.context.i);
});
Output:
1
1
They are, but AVA is just too fast :).
Ah, I see. That appears to be a bug to me.
He was pointing to the use case without the context object
Correct. In my actual use case, my tests are asserting that something else is calling console.log
Ah, I see. That appears to be a bug to me.
It's not a bug, running tests concurrently comes with a "cost", this is one of them. Take a look at the number example (2 posts above this one) again. It shows the behaviour you experience.
This is not something you can have in mocha (or others) because they are running tests serially and where the execution flow is easier to follow.
In my actual use case, my tests are asserting that something else is calling console.log
What I do if I have a similar use case is only running that particular test serially.
import test from 'ava';
import fn from './';
// Do spy stuff here
test.serial('test log', t => {
fn.logger('hello world');
t.true(console.log.calledOnce);
});
test('test something else', t => {
t.true(fn.somethingElse());
});
Ah, I think I understand now. This is part of how AVA helps (read: forces) you isolate your tests which is good. I'll submit a PR which mentions this.
Exactly. From the moment you have something shared (which might not seem to be shared in the first place), you will have to execute them serially.
@kentcdodds I believe this is the same problem that I was having with the shared ngMocks
state in Angular tests.
The workaround I came up with was to use after()
instead of afterEach()
. I've opened kentcdodds/ava-beforeEach-afterEach-bug#1 as an issue in your repo.