"top level" test in jest - node.js

While writing integration tests in jest I would like to reproduce the same behaviour I have achieved in mocha by:
mocha -r ts-node/register tests/integration/topLevelTest.test.ts 'tests/integration/**/*.test.ts'.
topLevelTest.test.ts :
let importantVariable;
describe("should do something with my variable", () => {
importantVariable = returnSomethingImportant();
it("should important variable exists", () => {
should.exist(importantVariable)
})
})
after(() => {
importantVariable.cleanUp()
})
Behaviour was simple: firstly topLevelTest executed describe, then other test suites executed themselves, and in the end after within topLevelTest were executed.
In my attempt of rewriting it to jest I wrote something very similar. Only difference is I used afterAll instead of after. The result is: firstly topLevelTest executed describe, then afterAll, and then other test suites. Is it possible to make afterAll run after other test suites?

This is what setup files are for, more specifically setupFilesAfterEnv because Jest environment is already initialized there with globals being available.
Top-level afterAll that wasn't grouped with describe applies to all tests within current test suite. Since Jest tests run in parallel (unless runInBand option was specified) in different threads, it obviously won't affect other test suites.
In case tests need to not proceed if a setup failed and data from setup needs to not be, globalSetup and globalTeardown configuration options should be used for that. This is not a test but the main difference is that describe and separate test blocks are unavailable. Global expect is not available but can be imported, this results in meaningful errors in case a setup fails:
// setup.js
let expect = require('expect');
module.exports = async () => {
let server = ...;
expect(server)...;
global.__MYSERVER__ = server;
};
// teardown.js
module.exports = async function () {
// close __MYSERVER__
};
Since global setup and teardown run in parent process, __MYSERVER__ cannot be accessed in tests.

Related

Is there a race condition using msw with jest?

I'm worried about race conditions w.r.t. MSW when multiple Jest test files run concurrently. Is that an issue?
The MSW getting started guide suggests the following code in setupTests.js:
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
But what if two tests run concurrently, and each starts with something like:
server.use(rest.get('https://api.backend.dev/user', (req, res, ctx) => {
return res(ctx.json({ firstName: 'Paul' }))
})),
or
server.use(rest.get('https://api.backend.dev/user', (req, res, ctx) => {
return res(ctx.json({ firstName: 'Ringo' }))
})),
Couldn't the handler set up in one test be called by the other test (or vice-versa)?
Do I need to use --runinband?
PS: Maybe I'm misunderstanding Jest because it seems like the "mock dom" implemented by jsdom would have the same issues with concurrently running tests.
I've at least convinced myself there's no race condition based on two facts:
Each Jest test file runs in its own subprocess. (This is mentioned in #5623 among other places.). So each test file should have its own server instance.
MSW on Node just monkey-patches functions like fetch() and XMLHttpRequest, rather than actually starting a web server. This is mentioned in #407. So, the monkey-patching should be isolated to each process running each test file.

Is there any way to add callbacks to Jest when all tests succeed or fail?

I'd like to run a callback when all the tests in a describe block pass (or fail), is there some hook or something in the Jest API to do this? I could not find anything applicable in the docs.
I'm making several API requests to collect data in order to compare it to data in a CSV file, in order to diff the contents. When the tests have all passed, I would like to save all the API responses in a file, therefore I need some sort of 'all tests passed' callback
You can run jest programmatically. Note that this approach is "hack" because there is no official support for running jest like this.
see: https://medium.com/web-developers-path/how-to-run-jest-programmatically-in-node-js-jest-javascript-api-492a8bc250de
There is afterAll that is aware of describe but runs regardless of test results. It can be used as a part of function to aggregate data from tests:
let responses;
testAndSaveResponses((name, fn) => {
if (!responses) {
responses = [];
} else {
afterAll(async () => {
if (!responses.includes(null)) {
// no errors, proceed with response processing
}
});
}
test(name, async () => {
try {
responses.push(await fn());
} catch (err) {
responses.push(null);
throw err;
}
});
});
It's supposed to be used instead of Jest test and be enhanced to support multiple describe scopes.
There is custom environment. Circus runner allows to hook test events, finish_describe_definition in particular. It is applied to all tests, unaware of custom data (e.g. responses that need to be saved) and should interact with them through global variables.
There is custom reporter, it receives a list of passed and failed tests. It is applied to all tests, unaware of custom data defined in tests and doesn't have access to globals from test scope so cannot be used to collect responses.

Sinon with multiple mocha test files

I have multiple Mocha test files using one shared base file known as testBase.js. It's responsible for setting up all stubs and spies.
If I run individual file through mocha all test cases pass but when it run tests through mocha *.js, test cases begin to fail and raise error
TypeError: Attempted to wrap send which is already wrapped
Here are my beforeEach and afterEach blocks
beforeEach(function (done) {
context.alexaSpy = sinon.spy(alexa, "send");
}
beforeEach(function (done) {
context.alexaSpy.restore();
}
I actually printed out logs in both blocks and there is a strange thing I noticed. I see logs this way
-- BeforeEach Fired Test1
-- BeforeEach Fired Test1
-- AfterEach Fired Test1
-- AfterEach Fired Test1
I don't know why it's calling twice and its the root cause of the issue. BefireEach must not call twice for one mocha test.
Does importing multiple files call beforeEach twice? Can someone suggest any possible solution to this? I tried sinon.sandbox too but it does not work
We need to see how you require in the base file to be certain.
My guess is simply that you require the file from multiple files, and each time you do this you add the setup and teardown functions. That happens because all the tests share the same outer scope. Requiring the Base file ten times will add the beforeEach ten times too.
The right way to do this would be using sinon.sandbox or sinon-test. Much easier to avoid one test interfering with the next.
But no matter what you do, you would need to export the function and run that in a beforeEach in each file
Typically like this
const base = require('./base')
describe('module one', ()=> {
beforeEach(base.commonStubs);
it('should.... ',..);
})

Jest clean up after all tests have run

Is it possible in Jest to run cleanup or teardown tasks that run after all other tests have completed? Similar to how setupFiles allows one to set up tasks after before any test has run. Bonus points if this can also run regardless if the test had any errors.
Putting afterAll(() => {}) at the top level of a file (outside any describe function) appears only to run after tests from that particular file have finished.
The use case is I have many test files that will create users in a a development database, and I don't want to make each test file responsible for cleaning up and removing the user afterwards. Errors can also happen while writing tests, so if the cleanup happens regardless of errors that would be preferable.
There's a sibling hook to setupFiles that will too fire before every test suite but right after your test runner (by default Jasmine2) has initialised global environment.
It's called setupFilesAfterEnv. Use it like this:
{
"setupFilesAfterEnv": ["<rootDir>/setup.js"]
}
Example setup.js:
beforeAll(() => console.log('beforeAll'));
afterAll(() => console.log('afterAll'));
setup.js doesn't need to export anything. It will be executed before every test suite (every test file). Because test runner is already initialised, global functions like beforeAll and afterAll are in the scope just like in your regular test file so you can call them as you like.
In jest.config.js:
module.exports = {
// ...
setupFilesAfterEnv: [
"./test/setup.js",
// can have more setup files here
],
}
In ./test/setup.js:
afterAll(() => { // or: afterAll(async () => { }); to support await calls
// Cleanup logic
});
Note:
I am using Jest 24.8
Reference:
setupFilesAfterEnv
To do some tasks after all test suites finish, use globalTeardown. Example:
In package.json:
{
"jest": {
"globalTeardown": "<rootDir>/teardownJest.js"
},
}
In teardownJest.js:
const teardown = async () => {
console.log('called after all test suites');
}
module.exports = teardown;
Keep in mind that jest imports every module from scratch for each test suit and teardown file. From official documentation:
By default, each test file gets its own independent module registry
So, you cannot share the same DB module's instance for each test suite or teardown file. Therefore, If you wanted to close db connection after all test suits, this method would not work
There looks like there is a feature called a reporter that just does exactly this:

Kicking off mocha describes in parallel

I want to be able to have all my describe statements in Mocha get kicked off in parallel. Can someone help me figure out how to do that?
You can't do this directly with mocha because it creates a list of it() callbacks and invokes them in order.
mocha-parallel-tests can do this if you're willing to move your describes into separate .js files. To convince yourself, install it somewhere and invoke it with a short --slow so it reports each time:
laptop:/tmp/delme$ npm install mocha-parallel-tests
laptop:/tmp/delme$ cd node_modules/mocha-parallel-tests
laptop:/tmp/delme/node_modules/mocha-parallel-tests$ ./bin/mocha-parallel-tests test/parallel/tests --timeout 10000 --slow 100
You will see that it ran three (very simple) tests suites in the time it took to run the longest.
If your tests don't depend on side-effects of earlier tests, you can make them all asynchronous.
A simple way to do this is to initiate the stuff that takes a while before the describe and use the regular mocha apparatus to evaluate it. Here, I create a bunch of promises which take a while to resolve and then iterate through the tests again, examining their results in a .then() function:
var expect = require("chai").expect;
var SlowTests = [
{ name: "a" , time: 250 },
{ name: "b" , time: 500 },
{ name: "c" , time: 750 },
{ name: "d" , time:1000 },
{ name: "e" , time:1250 },
{ name: "f" , time:1500 }
];
SlowTests.forEach(function (test) {
test.promise = takeAWhile(test.time);
});
describe("SlowTests", function () {
// mocha defaults to 2s timeout. change to 5s with: this.timeout(5000);
SlowTests.forEach(function (test) {
it("should pass '" + test.name + "' in around "+ test.time +" mseconds.",
function (done) {
test.promise.then(function (res) {
expect(res).to.be.equal(test.time);
done();
}).catch(function (err) {
done(err);
});
});
});
});
function takeAWhile (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(time);
}, time);
});
}
(Save this as foo.js and invoke with mocha foo.js.)
Meta I disagree with the assertion that tests should be primarily be synchronous. before and after pragmas are easier but it's rare that one test invalidates all remaining tests. All discouraging asynchronous tests does is discourage extensive testing of network tasks.
Mocha does not support what you are trying to do out of the box. It runs tests sequentially. This has a big advantage when dealing with an unhandled exception: Mocha can be sure that it happened in the test that it is currently running. So it ascribes the exception to the current test. It is certainly possible to support parallel testing but it would complicate Mocha quite a bit.
And I tend to agree with David's comment. I would not do it. At the level at which Mocha usually operates, parallelism does not seem to me particularly desirable. Where I have used test parallelism before is at the level of running end-to-end suites. For instance, run a suite against Firefox in Windows 8.1 while at the same time running the same suite against Chrome in Linux.
Just to update this question, Mocha version 8+ now natively supports parallel runs. You can use the --parallel flag to run your tests in parallel.
Parallel tests should work out-of-the box for many use cases. However, you must be aware of some important implications of the behavior
1 thing to note, some reporters don't currently support this execution (mocha-junit-reporter for example)
If you are using karma to start your tests, you can use karma-parallel to split up your tests across multiple browser instances. It runs specs in different browser instances in parallel and is very simple and easy to install:
npm i karma-parallel
and then add the 'parallel' to the frameworks list in karma.conf.js
module.exports = function(config) {
config.set({
frameworks: ['parallel', 'mocha']
});
};
karma-parallel
Disclosure: I am the author

Resources