Jest - Understanding execution order of a describe() and it() - javascript

I would like to understand why all the blocks describe() run before the it() in inside of each describe().
Here's a CodeSandbox example with a log per each describe and it block.
describe("sum A", () => {
window.innerWidth = 100;
console.log("describe A", window.innerWidth);
beforeAll(() => {
window.innerWidth = 105;
console.log("describe A before", window.innerWidth);
});
it("1. should add 1 and 2", () => {
console.log("it A1", window.innerWidth);
expect(1 + 2).toBe(3);
});
});
describe("sum B", () => {
console.log("describe B", window.innerWidth, "why does it run before it A1?");
beforeAll(() => {
window.innerWidth = 205;
console.log("describe B before", window.innerWidth);
});
it("1. should add 1 and 2", () => {
console.log("it B1", window.innerWidth);
expect(1 + 2).toBe(3);
});
});
You'll notice that the log from the second describe block runs before the it() inside the first describe block.
Why does that happen? Should we avoid doing stuff in that part of the code and prefer using beforeAll() when we need to share and scope data in that describe block?

When Jest runs it looks for all of the test files and runs each one.
Each test file runs within an environment provided by Jest that includes globals like describe, it, beforeAll, etc. All of these globals have a callback parameter that defines their behavior.
When a test file runs the top-level code runs...including any top-level describe calls.
When a describe runs it registers a test suite, and then its callback is called immediately.
This is different than it, beforeAll, beforeEach, etc. where the callback is recorded but not immediately called.
This means that all describe callback functions are called depth-first in the order they appear in a test file, as can be seen in this simple example:
describe('1', () => {
console.log('1');
describe('2', () => { console.log('2'); });
describe('3', () => {
console.log('3');
describe('4', () => { console.log('4'); })
describe('5', () => { console.log('5'); })
})
describe('6', () => { console.log('6'); })
})
describe('7', () => {
console.log('7');
it('(since there has to be at least one test)', () => { });
})
...which logs 1 - 7 in order.
This initial run of all the describe callbacks is called the collection phase during which the test suites are defined and all of the callbacks for any beforeAll, beforeEach, it, test, etc. are collected.
After the collection phase completes, Jest...
runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on.
For each test (each callback function registered with the global it or test functions) Jest links together any before callbacks, the test callback itself, and any after callbacks and runs the resulting functions in order.
Should we avoid doing stuff in that part of the code and prefer using beforeAll() when we need to share and scope data in that describe block?
For simple stuff that isn't shared it's fine to have it in the describe:
describe('val', () => {
const val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // Success!
});
});
...but code in the describe can cause issues with shared data:
describe('val', () => {
let val;
describe('1', () => {
val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // FAIL! (val gets set to 2 in the second describe)
})
})
describe('2', () => {
val = '2';
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
...which can be fixed either by using before calls:
describe('val', () => {
let val;
describe('1', () => {
beforeEach(() => {
val = '1';
});
it('should be 1', () => {
expect(val).toBe('1'); // Success!
})
})
describe('2', () => {
beforeEach(() => {
val = '2';
});
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
...or simply scoping the data to the describe:
describe('val', () => {
describe('1', () => {
const val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // Success!
})
})
describe('2', () => {
const val = '2';
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
In your example you are using window.innerWidth which is a shared global, so you will want to use before functions since it can't be scoped to the describe.
Also note that you can't return anything from a describe so if your tests require any asynchronous setup then you will need to use a before function where you can return a Promise for Jest to await:
const somethingAsync = () => Promise.resolve('1');
describe('val', () => {
let val;
beforeAll(async () => {
val = await somethingAsync();
});
it('should be 1', () => {
expect(val).toBe('1'); // Success!
});
});

Related

Jest mocking inside the same module

I'm following this thread. Mock a function called by a tested function of the same file with jest
functions.js
export const a = (x) => { a very complicated function };
export const b = (x) => exports.a(x+1);
functions.test.js
import * as functions from './functions';
describe('b', () => {
test('calling b calls a with x+1', () => {
functions.a = jest.fn();
functions.b(1);
expect(functions.a).toHaveBeenCalledWith(2);
});
});
It works for the most part, except that if I have additional unit test after describe(b), that required the original implementation of a(), it will still be treated as mock function, like let's say I want to unit test a(), it won't work because it is an empty function now. e.g
describe('c', () => {
test('unit testing a will call something 3 times', () => {
functions.a()
expect(whatever.get).toHaveBeenCalledWith(3);
});
});
any way to fix this?
Note:
I have tried the following, but it does not work
beforeEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
The only way I can make it work is this, but this seems like really hacky?
const originalA = functions.a.bind({});
afterEach(() => {
functions.a = originalA;
});
Have you tried using spyOn? It doesn't replace your implementation just chekcing what is happening with that function.
import * as functions from "../functions";
describe("b", () => {
test("calling b calls a with x+1", () => {
const functionASpy = jest.spyOn(functions, 'a');
functions.b(1);
expect(functionASpy).toHaveBeenCalledWith(2);
});
});

Node: how to test multiple events generated by an async process

I need to test an async Node.js library which periodically generates events (through EventEmitter) until done. Specifically I need to test data object passed to these events.
The following is an example using mocha + chai:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', async () => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler); // This handler should be called/tested multiple times
await asyncLib.start(); // Will generate several 'events' until done
await asyncLib.close();
});
});
The problem is that even in case of an AssertionError, mocha marks the test as passed and the program terminates with exit code 0 (instead of 1 as I expected).
The following uses done callback instead of async syntax, but the result is the same:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', (done) => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler);
asyncLib.start()
.then(asyncLib.close)
.then(() => done());
});
});
I have also tried with a "pure" Node.js approach using the native assert.ok without any 3rd part library:
const { strict: assert } = require('assert');
const { AsyncLib } = require('async-lib');
const test = async () => {
const onDataHandler = (data) => {
assert.ok(data.foo != null);
assert.ok(data.bar != null);
assert.ok(data.bar.length > 0);
};
asyncLib.on('event', onDataHandler);
const asyncLib = new AsyncLib();
await asyncLib.start();
await asyncLib.close();
}
(async () => {
await test();
})();
Even in this case, an AssertionError would make the program to terminate with exit code 0 instead of 1.
How can I properly test this code and make the tests correctly fail in case of an assertion error?
There are some things that you need to fix to make it works:
Make your test async, because the test is going to execute the expects after a certain event is received meaning it's going to be asyncronous.
Your event handler in this case onDataHandler should receive the done callback because there is the way how you can indicate to mocha that the test was finished successful as long as the expects don't fail.
I wrote some code and tested it out and it works, you have to make some changes to adapt your async library though:
describe('Test suite', function () {
const onDataHandler = (data, done) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
done();
};
it('test 1', async function (done) {
eventEmitter.on('event', (data) => onDataHandler(data, done));
setTimeout(() =>{
eventEmitter.emit('event', {
})
}, 400)
});
});

how can i place multiple test-suits in a single test-suit using Jest and supertest?

I have created multiple test-suites and want them to run under a single test suite or a single describe test block.
I have placed them like...
describe('',()=>{
require('test-suite1.test.ts')
require('test-suite2.test.ts')
require('test-suite3.test.ts')
...
....
}
Can anyone suggest me anyother way to replace the require and still run all the test under single test suite?
The layout of your tests can be something like this:
describe('All of the functions from this file', () => {
describe('first function', () => {
test('first function test one', () => {
// ... tests for the first function
});
test('first function test two', () => {
// ... tests for the first function
});
test('first function test three', () => {
// ... tests for the first function
});
});
describe('second function', () => {
test('second function test one', () => {
// ... tests for the second function
});
test('second function test two', () => {
// ... tests for the second function
});
test('second function test three', () => {
// ... tests for the second function
});
});
});
I stumbled upon this while looking for alternative ways of achieving this, but might as well share my current solution.
// suitOne.js
export const suitOne = () =>
describe('some functionality', () => {
it('tests smth', () => ...
}
// suitTwo.js
export const suitTwo = () =>
describe('some other functionality', () => {
it('tests smth else', () => ...
}
// allSuits.spec.js
import { suitOne } from './suitOne.js'
import { suitTwo } from './suitTwo.js'
suitOne()
suitTwo()
Test suits must be ran synchronously.

Package not activating when running specs

I've created a package for Atom called quick-fold that jumps to the next foldable line and folds it on the command quick-fold:fold-next. I wanted to start getting into Atom specs so I can run tests on this package, however I've hit this problem where the package is just never activated when running the specs.
quick-fold-spec.js:
describe('QuickFold package', () => {
let editor, editorView;
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
});
describe('when the specs are run', () => {
it('opens the sample file', () => expect(!editor.isEmpty()).toBe(true));
// true
});
describe('when the quick-fold:fold-next event is triggered', () => {
beforeEach(async () => {
// Try to activate package by dispatching command:
atom.commands.dispatch(editorView, 'quick-fold:fold-next');
await atom.packages.activatePackage('quick-fold'); // Never resolves
});
it('activates the package', () => {
expect(atom.packages.isPackageActive('quick-fold')).toBe(true);
});
it('moves the cursor to a different line number', () => {
expect(editor.getCursorScreenPosition().row).not.toBe(0);
});
});
});
But atom.packages.activatePackage('quick-fold') never resolves. The package doesn't activate and instead it times out:
timeout: timed out after 5000 msec waiting for spec promise to resolve
The activation command is set in package.json:
"activationCommands": {
"atom-workspace": "quick-fold:fold-next"
},
so dispatching this should activate the package, and then the await atom.packages.activatePackage('quick-fold') should resolve. But the cursor position doesn't change and the package doesn't get activated.
(Note that atom.packages.activatePackage('quick-fold') is merely a promise - it doesn't activate the package but it resolves when the package gets activated.)
As is often the case, I figured it out in the end...
1. The beforeEach() function is missing a runs()
It should be
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
runs(() => {
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
return activationPromise = atom.packages.activatePackage('quick-fold');
});
});
where the runs() function returns a promise on activation of the package.
From the docs:
Specs are written by defining a set of blocks with calls to runs, which usually finish with an asynchronous call.
I'm too tired right now to understand exactly what that means but it rings true here.
2. The activation commands in package.json should be based on atom-text-editor
I.e., not atom-workspace
"activationCommands": {
"atom-text-editor": "quick-fold:fold-next"
},
This is probably because we have atom.commands.dispatch(editorView, 'quick-fold:fold-next') where the commands are dispatched to editorView = atom.views.getView(editor) instead of the Atom workspace.
Refactored - the "standard" way of doing it
describe('QuickFold package', () => {
let editor, editorView, activationPromise;
const foldNext = async (callback) => {
atom.commands.dispatch(editorView, 'quick-fold:fold-next');
await activationPromise;
return callback();
};
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
runs(() => {
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
return activationPromise = atom.packages.activatePackage('quick-fold');
});
});
describe('when the specs are run', () => {
it('opens the sample file', () => expect(!editor.isEmpty()).toBe(true));
});
describe('when the quick-fold:fold-next event is triggered', () => {
it('activates the package', () => {
return foldNext(() => expect(atom.packages.isPackageActive('quick-fold')).toBe(true));
});
it('moves the cursor to a different line number', () => {
return foldNext(() => expect(editor.getCursorScreenPosition().row).not.toBe(0));
});
});
});

how to control order of execution of beforeAll blocks in describe blocks

I have a main test with sub-tests which follows the following structure:
describe('main test', () => {
describe('happy path', () => {
beforeAll(async () => {
console.log('in happy path...');
})
})
describe('treacherous path', () => {
beforeAll(async () => {
console.log('in treacherous path...');
})
})
})
This test, however, seems to log in happy path unconditionally even when setting that to skip using xdescribe. I was wondering what I can do to remedy this behavior.

Categories

Resources