Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to share global state between tests from globalSetup #7184

Open
dbartholomae opened this Issue Oct 16, 2018 · 9 comments

Comments

Projects
None yet
7 participants
@dbartholomae
Copy link

dbartholomae commented Oct 16, 2018

馃殌 Feature Proposal

Add a global property to the this of globalSetup and globalTeardown async functions that can be used to set global variables that can be accessed in all tests via global. 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:

const backend = require('backend')

async function setupApp () {
  await new Promise((resolve, reject) => {
    backend.start().then((instance) => {
      this.global.backend = instance
    })
  })
}

module.exports = setupApp

And using the global could be done like this:

const request = require('supertest')
test('should call out to that fancy other api', () => {
  request(jest.globals.backend.url)
    .post('/some-endpoint')
    expect(200)
})

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 even jest.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.

@SimenB

This comment has been minimized.

Copy link
Collaborator

SimenB commented Oct 16, 2018

First comments is that the setupfiles shouldn't assign to this. If we do this, I think the setup should return something, and we can assign that inside of jest itself (#5731 (comment)).

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)

@dbartholomae

This comment has been minimized.

Copy link
Author

dbartholomae commented Oct 16, 2018

I took the this pattern from environments. It seemed a bit odd to me, too, but would be consistent.

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 nock which attaches itself to its processes http module and therefore needs to be called in the same process as the one where the backend is running. It would be possible to set up some helper though I guess that communicates via serializable data. In that case we are talking more about inter-worker-communication then mere globals.

@SimenB

This comment has been minimized.

Copy link
Collaborator

SimenB commented Oct 16, 2018

Yeah, the envs are the ones that construct the global used in tests, so it makes sense that they have it. Doesn't mean it's a nice pattern, though 馃榾

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.

@dbartholomae

This comment has been minimized.

Copy link
Author

dbartholomae commented Oct 16, 2018

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.

@DanLambe

This comment has been minimized.

Copy link

DanLambe commented Nov 26, 2018

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".

@thomasacook

This comment has been minimized.

Copy link

thomasacook commented Jan 15, 2019

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 --runInBand always because we can't simply reuse external fake processes across tests, and it's not practical to test a single A microservice instance with various B and C dependency service instances running on random ports because each test is run in a separate process and can't access global node/http fakes of B and C. I hope this helps illustrate the issue at a high level better.

@jerimiah797

This comment has been minimized.

Copy link

jerimiah797 commented Feb 25, 2019

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. :-(

@jasonworden

This comment has been minimized.

Copy link

jasonworden commented Mar 1, 2019

Fans of this may like the newly opened Feature Request as seen right above :)

Allow module sandbox to be disabled via configuration #8010

@Phoenixmatrix

This comment has been minimized.

Copy link

Phoenixmatrix commented Mar 7, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can鈥檛 perform that action at this time.