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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to run tests within a file in a random order #4386

Closed
k-shan opened this issue Aug 29, 2017 · 34 comments · Fixed by #12922
Closed

Ability to run tests within a file in a random order #4386

k-shan opened this issue Aug 29, 2017 · 34 comments · Fixed by #12922

Comments

@k-shan
Copy link

k-shan commented Aug 29, 2017

Do you want to request a feature or report a bug?
Feature

There have been cases where a test passes but when reordering the test in a file it actually fails. To prevent this false positive it would beneficial to randomize the order of tests within a specific file.

Jasmine has enabled this (https://jasmine.github.io/2.8/node.html#section-22), would it be possible to expose a configuration to specify the order or just randomize it by default?

@cpojer
Copy link
Member

cpojer commented Aug 29, 2017

To isolate tests in Jest, you can use jest.resetModules() and then re-require modules for each individual tests. If you are strict about this, you won't need to randomize tests, and we aren't considering this feature for inclusion at this point.

@cpojer cpojer closed this as completed Aug 29, 2017
@felixc
Copy link

felixc commented Aug 29, 2017

Thanks for the pointer @cpojer, but as per #3236 it seems that this isn't actually possible when using ES6 import syntax. Is there anything in the works for that? I suspect increasing numbers of folks will be trying out the nice new imports...

@cpojer
Copy link
Member

cpojer commented Aug 29, 2017

Right, you'll have to use require calls inside of your tests, but the rest of your code can still use ES modules.

If you create your own ES module plugin that allows imports within functions (instead of only on the top level), you can use ES imports there as well. It's a small change to the flavor of babel that you are using, and would be limited to tests.

@thisismydesign
Copy link

thisismydesign commented Jan 19, 2019

In general, this would be a very useful feature that can root out many issues. I don't think the proposed workaround is realistic for existing bigger code bases. So +1 for this feature.

Edit: I haven't tested this myself yet but dynamic imports should be possible with babel-plugin-dynamic-import-node as shown here: #2567 (comment)

@theblang
Copy link

@cpojer Exposing the Jasmine 2 random config would actually be super helpful for us because we are using jest-image-snapshot along with Jest to write Puppeteer screenshot and integration tests, so being able to run a suite in random order would really help root out tests that might pass in a certain order but fail in isolation. I know that unit tests should never behave that way and thus it'd be a code smell in that class of tests, but Jest is also working great for us as a test runner for screenshot and integration tests where that behavior is more likely.

@SimenB
Copy link
Member

SimenB commented Feb 14, 2019

IIRC the the randomizing implementation was ripped out of jest-jasmine2

@theblang
Copy link

theblang commented Mar 6, 2019

@SimenB Do you know if that is something that would be considered to be put back in (i.e. would it be worth me trying to do a PR for)?

@jeysal
Copy link
Contributor

jeysal commented Mar 7, 2019

@theblang jest-jasmine2 is going to be phased out, jest-circus is planned to become the default in Jest 25. As for adding randomization to jest-circus, I personally don't think this is a good solution for discovering order-dependent tests, plus it would have to be opt-in or a huge breaking change.

@SimenB
Copy link
Member

SimenB commented Mar 7, 2019

I wonder if it makes sense as a feature for jest-circus the agnostic test runner, but not necessarily something jest the test framework does?

We have guarantees today about execution order within the files, and I highly doubt we'll break those, so it'd have to be opt-in, yeah.

@jeysal
Copy link
Contributor

jeysal commented Mar 8, 2019

Maybe there's some other arguments I didn't consider, but I think in the case of order-dependent tests randomization IMO makes the situation worse. I'd rather notice the inter-dependency when reordering some tests than have them run in an order that happens to pass a few times locally and on CI and then once it's in master on the 5th run they randomly break. The proper solution to discovering tests depending on order would be to run all possible orders, which is obviously not feasible.

@felixc
Copy link

felixc commented Mar 8, 2019

As an opt-in feature it would still be hugely helpful!

Since yes, running all possible orders is not feasible, I'd be curious to hear about what other strategies folks have other than randomization for discovering these bugs in their tests.

@theblang
Copy link

theblang commented Mar 8, 2019

@felixc What I'm about to suggest isn't very pertinent to the ticket since suites are already run in a random order, but one thing we do with our screenshot tests (which utilize Puppeteer and jest-image-snapshot) is close and reopen Puppeteer between each suite run. Then at least you are getting clean client state between each suite run. Before Puppeteer we were using CasperJS without a proper test runner and would use the same instance for all the suites, which could get really annoying when a test change in one suite affected an entirely different suite. And that would happen a fair amount since the suite order wasn't random either and client state could linger between suites.

@jeysal The reason that randomization helps is that the screenshot test causing the problem will be discovered much sooner, potentially even during the development of the feature branch that the problematic test is written in if your CI tests are running for branches. Then the test can hopefully be addressed closer to writing the feature that it was initially for, and with the various state and code involved fresh on the mind. As it stands right now, intermittent failures can collect for a long time then creep up on someone down the road when they make a change that somehow disturbs the order and exposes the problematic test.

I definitely want to highlight the screenshot word, because this is mostly an issue with doing integration / screenshot tests where there's a lot of state, side effects, and other things going on, and where you can't easily reset state between each test. It's easy to unknowingly mutate something that lingers and affects other tests when writing this class of tests.

@SimenB
Copy link
Member

SimenB commented Mar 19, 2019

test.each(randomize([
  ['title 1', ()=> { setup(); return screenshot() }],
  ['title 2', ()=> { setup2(); return screenshot() }],
]))('%s', (_title, func) => func());

Written in freehand on a phone, so excuse syntax/API errors.

Would something like that work you?

@alex-hall
Copy link

alex-hall commented Mar 22, 2019

+1 for the ability to run tests in a random order.

I understand best practices like clearing mocks between tests and such, but i've run into a lot of test pollution or even tests accidentally passing due to certain data being created (persistent stores like local storage comes to mind). Randomizing tests helps engineers not be bitten by test number 3 creating data/state that test 1034 relies upon and having no real way of tracking down what's going on.

I'd be interested in taking a stab at a PR with the idea that the whole thing would be opt in and wouldn't break any existing contracts/tests.

I'd also like to propose that if a random run were to be accepted that it also be shipped with a deterministic seed value so you could run a bisect over the suite to figure out the minimum number of tests that would re-produce the problem.

@mockdeep
Copy link

Likewise. RSpec in Ruby has randomized test order and it has helped us find a lot of tests that inadvertently impacted others for one reason or another. When I go about refactoring Jest specs I start to come across these sorts of issues where the test is passing when it shouldn't be due to previous tests mistakenly modifying some global state. When RSpec runs randomized, it prints out the seed which you can use to reproduce an issue and fix it.

@theblang
Copy link

theblang commented Jan 1, 2020

@cpojer @jeysal @SimenB Just wanted to see if there's a possibility of this feature being reconsidered after the amount of interest that has been expressed, and the benefits it could bring to the visual and integration testing domains (for which some nice tools exist that make use of Jest, like jest-image-snapshot from AmEx).

@jeysal
Copy link
Contributor

jeysal commented Jan 26, 2020

I am still of the opinion that even as an opt-in this would do more harm than good across all the users that would enable it, based on the reason I stated above.
If I really really wanted this, I'd probably enable jest-circus (see docs) and write a custom event handler that does what it tells you not to do: on finish_describe_definition shuffle (mutate) (currentDescribeBlock.children.slice(-1)[0] || currentDescribeBlock).tests (and .children to also randomize order of describe blocks).
I think it goes without saying that this must be extremely important to your project to warrant relying on things that could easily break in the future - I am already hating myself for giving tips on how to best do this 😅 but I empathize with the situation some of you may be in and would rather you don't go with an even more fragile approach.

@gregplaysguitar
Copy link

@jeysal I don't understand how randomising the run order would make things more fragile - on the contrary, doesn't it force our tests to be more robust, to ensure they don't break when the run order changes?

The key issue IMO is reproducibility - i.e., when the tests do fail due to ordering, there needs to be a way to reproduce that order. @alex-hall's suggestion hits the nail on the head there:

if a random run were to be accepted that it also be shipped with a deterministic seed value so you could run a bisect over the suite to figure out the minimum number of tests that would re-produce the problem.

My 2c, I would imagine it working like this: a new --random flag added which randomises the test runs, and optionally accepts a seed, i.e.

jest --random
jest --random {SEED}

The seed would be auto-generated if not provided, and if the tests fail, the seed value used would be part of the output - so they could easily be reproduced.

@SimenB
Copy link
Member

SimenB commented Feb 13, 2020

If we were to ship this, it would 100% be with a seed that can be set and that'd be prominently printed if tests fail.

I think it's far less likely to cause hard to track down errors than say test.concurrent which we have today. Those are way more nondeterministic than a seeded "randomization" of test order.

@alex-hall with no promises on it being merged (although I personally would use such a feature at work 😅), are you still open to taking a stab at this? At least an initial exploratory try at an implementation might reveal if it slots nicely into Circus or not.

@thisismydesign
Copy link

@jeysal You sound like you take this as some experimental idea. It's not. It's been around as practically a standard in many other frameworks. In most places I've seen it's considered a code smell not to run tests in random order. There's no question about the validity of this feature.

To address your specific concern, most order-dependent tests will fail right away or within the first few tries and the devs will learn not to write them anymore. In the rare cases, it can root out subtle edge cases and bugs. I was never unhappy to have found them.

@alex-hall
Copy link

@SimenB lll do some jest archaeology and take a look this weekend.

@SimenB
Copy link
Member

SimenB commented Feb 14, 2020

Wonderful, thanks! Feel free to open an early PR with hacky WIP code for feedback as you go. 🙂
Also note that just adding it to jest--circus is fine (maybe even preferred) - we'll be phasing out jasmine (although it'll probably stick around for a long time yet).

@SimenB SimenB reopened this Feb 14, 2020
@langri-sha
Copy link

This would be an incredibly useful feature! I wouldn't also mind if this could be implemented by simply running tests within a module concurrently, because it would help expose other kinds of problems as well.

@aledalgrande
Copy link
Contributor

Wondering if random order has been worked on? I couldn't find any reference to it in the Jest circus code.

@pke
Copy link

pke commented Oct 19, 2021

@alex-hall did you find some time to start something (that might be continued by others)? I am currently stuck with a jest-fetch-mock based test that runs only in one particular order, which drives me crazy.

@jhwang98
Copy link
Contributor

jhwang98 commented Jun 7, 2022

@SimenB I don't see a PR by alex-hall on this. It was pretty easy making use of lodash.shuffle to make this work.
Of course I don't have an opt-in cli argument nor a way to generate seeds (Math.random doesn't provide such).

I'm not sure if this repo wants to make lodash a dependency or to implement a shuffle algorithm and pseudo-random number generator internally.

@aledalgrande
Copy link
Contributor

@jhwang98 lodash is not the right solution I think, as you can't pass in a seed which means you're not gonna be able to rerun a test suite in the same order to debug issues with your tests.

@jhwang98
Copy link
Contributor

jhwang98 commented Jun 8, 2022

I absolutely agree, I've added a pseudorandom number generator and a shuffling algorithm to jest-util.
It reproduces the random numbers.
Let me know if you think the approach still needs work.
If not I'll try expose the seed through the CLI.

@SimenB
Copy link
Member

SimenB commented Oct 14, 2022

https://github.com/facebook/jest/releases/tag/v29.2.0 has shipped with support for seeding. While randomization is not included out of the box (for now, at least), this can be used to implement a solid solution in userland (I think).

@SimenB
Copy link
Member

SimenB commented Mar 6, 2023

https://github.com/facebook/jest/releases/tag/v29.5.0

@pke
Copy link

pke commented Mar 7, 2023

Thanks @SimenB!
Could you please give a hint how that could be used to randomise the test order?

@cmdcolin
Copy link

cmdcolin commented Mar 7, 2023

@pke I believe you can add --randomize to the CLI, so e.g. if you have a test script in the package.json, you can replace

"scripts": {
   "test": "jest"
}

with

"scripts": {
   "test": "jest --randomize"
}

or just run yarn test --randomize without editing the package.json

@SimenB
Copy link
Member

SimenB commented Mar 7, 2023

yep: https://jestjs.io/docs/cli#--randomize

tkrotoff added a commit to tkrotoff/MarvelHeroes that referenced this issue Mar 9, 2023
tkrotoff added a commit to tkrotoff/fetch that referenced this issue Mar 10, 2023
@github-actions
Copy link

github-actions bot commented Apr 8, 2023

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.