-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
Allow to share global state between tests from globalSetup #7184
Comments
First comments is that the setupfiles shouldn't assign to Also, I think anything assigned will need to be serializable, I don't think it's technically possible for it to be e.g. an instance of something (we need to send it to workers which is a separate node process) |
I took the If the object needs to be serializable, then unfortunately a lot of merit of this feature would be lost. My main usecase would indeed be using |
Yeah, the envs are the ones that construct the Due to a hole in the sandbox (we give you the real core and native modules) nock should work. Note that it might break at any time, as that's a bug. |
It should? Interesting, I'll give it another try. Last time I didn't get it to work. Basically what this feature proposal is about is providing a sanctioned way to do this. |
I agree with @dbartholomae on this issue, I find it hard to recommend jest for all types of testing without the ability to share state between tests. I have a real usecase currently where the company I work for wanted to standardize our testing frameworks so I do to start using Jest over Mocha for my functional API testing for our react app. that was a mistake given that I have to fetch a new bearer token for every test file with no way of retaining that token to a variable "globally". |
I also agree with this issue - my team is using Jest/Supertest to test APIs for a microservice, and external service dependencies are faked using node/http. The setup is fantastic other than we have to use |
My use case involves testing mobile devices with Appium. Without a global handle to the chromium webdriver (which connects to the device through the appium server and installs the app), each testfile must repeat this process of setup and teardown of the app. It adds up to 40 seconds for each testfile to go through this process. As it stands right now, I also have to --runInBand of course since otherwise the tests will all try to instantiate their own chromedriver connection at the same time. I have seen some really gross workarounds to this problem that abstract the various tests in each testfile into regular js functions, and then make you call all the functions inside a single shell test.js file that contains the describe/it structure. I would really prefer not to do this since it breaks the ability to run a specific testfile on demand by passing the test as a CLI argument. :-( |
Fans of this may like the newly opened
|
Just adding our use case: We have an asynchronous initialization step that we need to do as a one time setup (and then expose the result to individual tests). The initialization is expensive and really should only happen once for the duration of the whole test run, but as it is, without runInBand, it's not possible. |
it would be very useful for us as well |
+1 here, there's some backend setup we'd like to share across all suites |
Note that you'll never be able to share things that are not json-serializable as we have no way of passing that to workers. But sharing e.g. URLs to services will work whenever we get around to this. So things like chromedriver connections talked about above cannot be supported. Puppeteer deals with this through exposing a websocket: https://github.com/smooth-code/jest-puppeteer/blob/master/packages/jest-environment-puppeteer/src/global.js |
This is a deal breaker, something has to be done for sure. Most of these apps take 20secs or more to bootstrap and having that happen for 30 or 50 times (once for each test file) is a big no no. It should only happen once as stated above. Can't Jest pass state to it's child processes or something along those lines. It'd be ok if all test files could just access even the master worker's global state. |
No, that's not how communication between processes work: https://nodejs.org/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback It's a bit better with worker_threads for builtin primitives, but not much: https://nodejs.org/api/worker_threads.html#worker_threads_port_postmessage_value_transferlist This isn't an API choice Jest has made, it's a fundamental technical limitation. Puppeteer allows connecting to a running instance through a websocket, you need to do something similar for whatever thing you're instantiating in a |
can you give a working example please? Whatever workaround you have to pass around instances, can't it be integrated into Jest or at least documented in Jest docs. |
I don't know in how many ways I can say this, but I'll try one last time: you cannot pass around instances, it's not possible. And seeing as this issue is still open, we have no solution for passing anything else either. Please don't keep asking about things I've stated multiple times are not possible, or I'll have to lock this issue
I recommend asking on StackOverflow or our discord channel for help. |
take puppeteer for example:
|
@SimenB would it be feasible to not use worker processes, given some flag? |
So I'm trying to understand - most of this discussion is how to handle non-serializable items, but where did we leave off on allowing serializable items from globalSetup?
There's not a good solution currently for Jest as the |
I have the same use case as @adrianmcli any info? |
@adrianmcli @brianschardt // entrypoint.test.js
beforeAll(async () => {
global.server = await startServer();
});
import './tests/my-first-test';
import './tests/my-second-test';
import './tests/other-test'; // ./tests/my-first-test.js
test('my test', () => {
expect(global.server.doSomeIntereaction()).toBe(...)
}) Of course, this does not come with parallelization. |
I think that #8708 would solve a bunch of problems people have that motivate this kind of state sharing. Instead of having to share something from the global setup down to the child processes, each of those child processes should (probably?) own one and only one of the resources. A puppeteer, a database connection, a booted server, etc, should be one per worker, not one global one per test run, and also not one per test file. That plays nicest with the automatic parallelization, keeps things fast, and I think is semantically sound with what Jest does already. Would that work for you folks and if so please thumbs up #8708! |
Right, that's already possible with I think we can say there are many levels and contexts of setup:
There is a process boundary between the per-invocation and per-worker layers that allows for parallelization and the Jest authors have said won't go away. That's the right move IMO. That means you can't ever pass real objects and especially not real open sockets down from the one top places to N inner places. What I am talking about is giving Jest users the ability to run setup once for the worker instead of once per suite. Connecting to ES or Redis or what have you once per suite isn't the end of the world, but for services that don't have multi-tenancy built in like puppeteer, or for app-land code that is expensive to boot, it'd be nice to do it once per worker instead of once per suite. Say creating kafka topics for the worker to use, or creating a Postgres database like I also think tearing down Kafka / ES from jest isn't the best idea -- teardowns are more best effort than gauranteed IMO. You can't really guarantee that the teardown runs because a dev could SIGTERM the process, or it could OOM, or whatever really. The best use of those backend services would be re-entrant and automatically massage whatever state is in them into the clean state necessary for the test. |
For people that just want to pass primitives like strings from setup file to tests you can do that using environment variables process.env.FOO = 'bar' But yeah this is a serializable state. |
Sharing global states is possible thanks to configuration's property testEnvironment
And voilà ! |
Working : Tests_Environment to be set in jest.config.json's testEnvironment property Global can be shared implementing setup : this.global.my_prop = global.my_prop; More info : jestjs/jest#7184 (comment) Tests with an http server : TestsServer_Environment Not working. Maybe using Worker ? But this way objects must be serializable
@MarquonsDesCompetences-ThiB I didn't test it, but I think that the setup function is called for each test accordiing to the doc
In my case, I have two scripts to run tests
// package.json
{
// ...
"scripts": {
// ...
"test": "jest --forceExit --detectOpenHandles ./tests/index.test.js",
"test-only": "jest --forceExit --detectOpenHandles --runInBand"
},
"jest": {
"testPathIgnorePatterns": [
"/node_modules/",
".tmp",
".cache"
],
"testEnvironment": "node",
"setupFilesAfterEnv": [
"./tests/helpers/setup.js"
],
"globals": {
"__APP__": undefined
}
}
// ...
} in my setup.js, I setup the global app, for every test, so when I run the // tests/helpers/setup.js
beforeAll(async (done) => {
// setup the app
global.__APP__ = await setupMyApp();
done();
});
afterAll(async (done) => {
// teardown the app
await teardownMyApp()
global.__APP__ = undefined;
done()
});
// tests/index.test.js
const glob = require('glob');
const path = require('path');
it('app is defined', () => {
expect(global.__APP__).toBeDefined();
});
glob.sync('./tests/**/*.test.js').forEach(function (file) {
require(path.resolve(file));
}); If I want to execute test suites in separate way, I use the |
But with all tests running under one test suite, you lose out on the ability to scale any integration tests with more than one database. Now everything is lumped together instead of being completely isolated. |
I would kindly ask also The use case is about sharing a read only dataset (1..10kB) with tests. I'm currently pondering between temporary files (yuck!) and environment variables. The size limits of env.vars keep me slightly concerned, and a standard mechanism provided by Jest would be preferred. |
If somebody wants to try the expected behaviour please read this comment. |
I also am looking for this feature. We are trying to set up a singleton mongoMemoryServer instance (with individual dbs per test suite) but have not been able to get the mongo instance from globalSetup accessible in the environment setup. |
This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days. |
Remove stale label |
Based on this thread, it appears that it should not be possible to use global variables in tests if those variables were set up in Based on this thread this isn't the behavior one should expect to see, so I worry that I can't rely on my setup continuing to work. I've tried this both with --runInBand and without with the same behavior. I've tried setting global context by using Here is some example code:
|
We have this working:
We make sure the db is running before starting the tests and just start/stop the server. It's not particular great to have test dependencies if the runner isn't aware of them (as it will just error out where instead it should just not run tests) ... but surprisingly it's the work around we managed to live with for the past few years. HTH |
One of the reasons I want to see this feature implemented is that in my tests, I require a module that takes some time to initialize due to its side effects, as exemplified here (this is simple example but there are module that does intensive calculations to pre-calculate curves). When I run the application, this initialization is not a problem because the module initializes only once. However, in isolated test environments, requiring such a module consumes time in every test, even though it would be completely safe to share the initialization between them. I found an article how to use jest env to share cached module https://www.petecorey.com/blog/2018/11/05/bending-jest-to-our-will-caching-modules-across-tests/, but it works only inside same test file, because env initialize per file |
Jest is a great framework that does a lot of things really well. But this is a pretty big, even deal-breaking limitation for some teams. At the very least this limitation/feature should be clearly documented and visible before teams get too far into Jest. Maybe that's already the case, and I've just done a terrible job of reading the docs, but it wasn't something I was aware of. I know this doesn't work for every case here, but for the auth token setup, if you check whether your token variable is already defined before re-execution, you would technically only run the token generation API requests once.
|
Really sad, today 2024 didn't have a choice to share an instance between tests. I'm looking alternatives to jest right now |
🚀 Feature Proposal
Add a
global
property to thethis
of globalSetup and globalTeardown async functions that can be used to set global variables that can be accessed in all tests viaglobal
. The same global is shared across all tests.Motivation
While jest was in the beginning used only for frontend testing, it has moved in the direction of becoming a general test framework. Especially for backend integration tests there is a tradeoff between test speed and departmentalization: Starting up a new backend instance for each individual test usually isn't feasible. Therefore most other test frameworks like mocha or jasmine provide possibilities to share state between tests, e. g. the backend instance. Usage examples include mocking http requests via nock.
Example
Let's assume an integration test that tests backend and database integration. The setup could look like this:
And using the global could be done like this:
Pitch
As far as I know this change currently cannot be implemented outside of the main jest framework. Closest is an environment, but environments are sandboxed and do not share global state.
Open questions
How to best implement it?
I don't know the jest code well enough to have an idea how to best implement this. It might e. g. be easier to make the global available via
global
, or evenjest.getGlobals()
.Can we prevent misuse?
Sharing state between tests can lead to sideffects and random test breakage. One possible solution would be to make the
jest.globals
read-only, but I am not sure whether this is feasible without massively reducing which kind of objects can be stored.The text was updated successfully, but these errors were encountered: