I have a Node.js project that I'm testing using Jest. I have several test files that have the same setup requirement. Previously, all these tests were in one file, so I just had a beforeAll(...) that performed the common setup. Now, with the tests split into multiple files, it seems like I have to copy/paste that beforeAll(...) code into each of the files. That seems inelegant - is there a better way to do this, ideally where I can just write my beforeAll(...)/setup logic once, and "require" it from multiple test files? Note that there are other tests in my test suite that don't require this setup functionality, so I don't want to make all my tests run this setup (just a particular subset of test files).
If you're using Jest >=20, you might want to look into creating a custom jest-environment for the tests that require this common setup. This would be a module that extends either jest-environment-node or jest-environment-jsdom, and implements async setup(), async teardown(), and async runScript() to do this setup work.
You can then add a #jest-environment my-custom-env directive to those files that require this setup.
See the Jest config docs for testEnvironment for details on how to set this up; there's a simple example there.
I am using a simple "test hooks" pattern for this:
// This function wraps beforeAll and afterAll into a single RAII-like call.
// That makes the describe code further down easier to read and makes
// sure you don't forget the afterAll part. Can easily be shared between tests.
function useFakeServer() {
let server;
beforeAll(() => server = sinon.fakeServer.create());
afterAll(() => server.restore());
return () => server;
}
describe('Some scenario', () => {
const getServer = useFakeServer();
it('accesses the server', () => {
const server = getServer();
// Test as you normally would..
expect(server.requests[0]. /* ... */);
});
});
If you need a script to run before all your test files, you can use globalSetup
This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites.
in your jest.config.js
//jest.config.js
module.exports = {
...
testTimeout: 20000,
globalSetup: "./setup.js"
};
then create a file named setup.js
// setup.js
module.exports = async () => {
console.log("I'll be called first before any test cases run");
//add in what you need to do here
};
Docs
You can move your beforeAll logic into one file and reference it in jest.config.js setupFilesAfterEnv section:
module.exports = {
...
setupFilesAfterEnv: ['<rootDir>/testHelper.ts'],
...
}
https://jestjs.io/docs/en/configuration#setupfilesafterenv-array
Create a function somewhere like so:
export function setupBeforeAndAfter(putParamsHereIfYouHaveAny) {
beforeAll(() => shared-before-all-code);
afterAll(() => shared-after-all-code);
beforeEach(() => shared-before-each-code);
afterEach(() => shared-after-each-code);
}
Then just call it wherever you would otherwise have manually written these functions:
describe('My test', () => {
setupBeforeAndAfter(putParamsHereIfYouHaveAny)
it('is amazing', () => {
// Stuff in setupBeforeAndAfter() will run before/after this test as appropriate
})
})
Related
I am trying to mock the useNavigate hook using Jest. I was able to do it at the top level of my test suit (I also have a custom render with BrowserRouter as a wrapper)
const mockedUsedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => mockedUsedNavigate,
}));
...
expect(mockedUsedNavigate).toHaveBeenCalledWith('myurl/user-1');
My issue is that I want to have this mock available inside the __mocks__ directory so I can use it in other tests as well instead if having to write it again and again. I wasn't able to do it.
Is it possible?
You should be able create a src.setupTests.js file in your project and declare the mock there.
See Initializing Test Environment
src/setupTests.js
export const navigateMock = jest.fn(); // <-- exported for test assertions
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => navigateMock,
}));
You can then import the exported navigateMock function in test files.
I have a file that uses a webworker that I want to unit test. But because Jest is flaky on ESModules, it doesn't like import.meta.url.
I don't want to test this part, I need to test the functionality around it. But because it's still included in the build, js-test keeps tripping over having this included in the build.
I abstracted it into a separate JS file worker-loader.js:
const spawnWorker = () => {
return new Worker(new URL('./worker.ts', import.meta.url));
};
module.exports = { spawnSqlJsWorker};
Which is use with a require in the main source code.
I tried adding a mocks directory with an empty mock:
const spawnWorker = () => {
return null;
};
module.exports = { spawnSqlJsWorker};
And ignore this file from jest in jest.config.js:
transformIgnorePatterns: ['worker-loader.js'],
But the loader keeps wanting to include this file. Is there a way in jest to completely ignore a file and mock the complete contents of it?
I'm writing e2e tests using Jasmine and Protractor with AngularJS. I have a parent describe which describes the page, and some setup call in beforeAll that goes to the correct page for the following tests. I've broken these tests up into multiple describes per feature. Here is an example:
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describe('Location Section'), () => ...
describe('Information Section', () => ...
The problem I'm having is that there are a lot of features within this module, and the tests are starting to push 300-400 lines of code. I would ideally like to put the nested describes in seperate files and import them. I've done something like this:
const describeLocation = require('./folder/location'),
describeInformation = require('./folder/information');
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describeLocation();
describeInformation();
In the other files I'm simply exporting an anonymous function and including the nested describe's code. This works, but unfortunately the tests don't have the jasmine context (can't access anything in beforeAll).
I'm curious if there is a standard or better way to accomplish this?
//export.js
describe(...
)
//Import or your main specs file
describe('',()=>{
require('./export');
})
Don't export in a form of a method just write your spec and import it using require in the main describe.
I want to use shared resources between jest test suites. I read in the internet and found that this could be the solution. But the setup is invoked per each test file.
I have two test files links.test.js and 'subscritpions.test.js'. I usually call them with one command jest and that all.
The problem is that the setup function of my custom environment custom-environment.js:
const NodeEnvironment = require('jest-environment-node');
const MySql = require('../../lib/databases/myslq/db');
class CustomEnvironment extends NodeEnvironment {
constructor(config) {
super(config)
}
async setup() {
await super.setup();
console.log(`Global Setup !!!!!!!!!`);
this.global.gObject = "I am global object"
this.global.liveUsers = await new MySql("Live Users");
this.global.stageUsers = await new MySql("Stage Users");
}
async teardown() {
console.log(`Global terdown !!!!!!!!!`);
await super.teardown();
this.global.gObject = "I am destroyed";
this.global.liveUsers.closeConnection();
this.global.stageUsers.closeConnection();
}
runScript(script) {
return super.runScript(script)
}
}
module.exports = CustomEnvironment;
is called twice for each test:
Global Setup !!!!!!!!!
Global Setup !!!!!!!!!
ERROR>>> Error: listen EADDRINUSE: address already in use 127.0.0.1:3306
So it tries to establish second connection to the same port - while I could simply use the existing connection.
The way it works seems to me makes no difference from defining
beforeAll(async () => {
});
afterAll(() => {
});
hooks.
So to wrap up, the question is: Using jest command (thus running all test suits), how can I invoke setup function once for all test and share global objects across them?
setup and teardown are indeed executed for each test suite, similarly to top-level beforeAll and afterAll.
Test suites run in separate processes. Test environment is initialized for each test suite, e.g. jsdom environment provides fake DOM instance for each suite and cannot be cross-contaminated between them.
As the documentation states,
Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment.
The environment isn't suitable for global setup and teardown. globalSetup and globalTeardown should be used for that. They are appropriate for setting up and shutting down server instances, this is what documentation example shows:
// setup.js
module.exports = async () => {
// ...
// Set reference to mongod in order to close the server during teardown.
global.__MONGOD__ = mongod;
};
// teardown.js
module.exports = async function () {
await global.__MONGOD__.stop();
};
Since this happens in parent process, __MONGOD__ is unavailable in test suites.
i try to use the mocha utils stackTraceFilter() function
but i cannot find an example usage case where someone explains how to use it in ones test. I found the official tests here: link
But how can i implement it in my tests, which somehow look like that:
import { expect } from 'chai'
import 'mocha'
import { main, main2 } from './'
describe.only('index.ts', async () => {
it('should start a job', async () => {
// const R_RUN_MAIN = await main()
await main2()
// TEST
expect(1).to.equal(1) // fails
})
})
In the tests i can see the line
expect(filter(stack.join('\n')), 'to be', stack.slice(0, 3).join('\n'));
But how do i get the Stack for my test?
expect(1).to.equal(1) // fails
or in general, how do i get the stack and initialize the filter function for the whole file when, for example, code from an imported file is already failing and creating a long stack trace?
UPDATE (2018.08.15)
so i got mocha running in a programmatic way:
export {}
import * as MOCHA from 'mocha'
async function run() {
const mocha = new MOCHA({
reporter: 'progress',
reporterOptions: {
verbose: true,
},
})
mocha.addFile(`./src/utils/mocha/index.spec.ts`)
const R = mocha.run((failures) => {
process.on('exit', () => {
process.exit(failures)
})
})
}
run()
I dont know where to add and run the Filter function?
const filter = MOCHA.utils.stackTraceFilter
The stackTraceFilter() function in mocha isn't meant to filter your code, but rather the mocha internals that in theory shouldn't be relevant to your tests. You can view the source code, but to sum it up it just filters out 'mocha' and 'node' lines from the stack, depending on the environment you're in.
I think what you're looking for could be accomplished through the package StackTraceJS, which allows you to grab a stack from anywhere, and do what you want with it. We created a custom reporter for mocha which uses it, and it works quite well.
So, using the example from their site:
StackTrace.get()
.then(function(stack){
// you now have a stack, and can filter as you wish
})
.catch(function(err){});