mocha icon indicating copy to clipboard operation
mocha copied to clipboard

--order option for random test order?

Open sarenji opened this issue 12 years ago • 68 comments

An --order option would allow people to uncover order dependencies. The three options would be --order random, --order random:seed, and --order default. Each randomized suite outputs the seed it used.

RSpec implements this, but their default order is random. Mocha doesn't have to do that. Some details on their --order parameter are here: http://blog.davidchelimsky.net/2012/01/04/rspec-28-is-released/

What do you think?

sarenji avatar Jun 18 '13 14:06 sarenji

meh, pretty easy to avoid cross-test dependencies without help of tooling

tj avatar Jul 01 '13 21:07 tj

While it is easy to avoid cross-test dependencies without help of tooling, it is also easy to add such a dependency in a test suite without noticing. A bug in the test suite usually translates to a bug in a tested system. These bugs are hard to trace, as the code is supposedly test-covered.

Testing for absence of cross-test dependencies is impossible without tooling.

@visionmedia, please reconsider.

yanovich avatar Jan 30 '14 09:01 yanovich

+1 @yanovich. I would use a random order option which outputs a seed number. This would be very useful in a CI environment.

@visionmedia, mongoose models provide an easy example of cross-test dependencies. mongoose.model 'User', UserSchema adds a model onto the array of mongoose.models. So it's possible to create a file which relies on the user model being loaded in mongoose.models. Take Comment.find().populate('_user').exec(cb) as an example. If the user test runs before the comment test, this will execute fine, because presumably require('./models/user') (or something), has loaded the User model into mongoose.models. But if the comment test executes before the user test you'll get this error Schema hasn't been registered for model "User". This could happen in production when the comment api runs before the user api and the comment file didn't know it had a cross file dependency.

It is possible to still have the production problem with the test working if the test file has require('./models/user') (or whatever) and that loads the user into mongoose.models. However having a random order would be one more useful tool to discover potential problems like this.

I hope articulated that well. Looking forward to hearing your thoughts.

trshafer avatar Feb 07 '14 03:02 trshafer

sorry, I think it's major overkill, mocha is bloated enough as-is. If there was a lot more interest then perhaps it would be worth the maintenance burden.

tj avatar Feb 07 '14 03:02 tj

Thanks for thinking about it.

trshafer avatar Feb 07 '14 04:02 trshafer

Like most things in code, it's easy for people who know to avoid doing this intentionally. It's harder to avoid doing it unintentionally. And if you don't know it's even a problem (i.e mixed experience teams) it's downright likely to happen :)

Seems like quite a few ppl are interested in it (and lots think it's one of the best features of minitest). If it'd get merged I'm happy to implement.

timruffles avatar Feb 25 '14 15:02 timruffles

+1 interested.

MaerF0x0 avatar May 16 '14 17:05 MaerF0x0

Would be nice to have! I found my tests to fails by renaming file names, ugh.

Rush avatar Jun 06 '14 10:06 Rush

+1 this is important

crismali avatar Jun 10 '14 13:06 crismali

:+1:

OliverJAsh avatar Jun 16 '14 16:06 OliverJAsh

:+1:

shlima avatar Aug 23 '14 11:08 shlima

+1 This is a pretty big deficiency.

rspec semantics are pretty solid: you can pass an order seed, or it can pick it at random. If it picks the seed at random, it prints it out, so it's easy to reproduce.

syrnick avatar Aug 26 '14 00:08 syrnick

It's often not that easy to avoid cross-test dependencies. Sometimes due to unforeseen global interactions, sometimes out of convenience. I suspect more than 50% of projects that use mocha would see test failures if the order was randomized. Here's a couple examples that appear to rely on the order of the test execution:

https://github.com/visionmedia/mocha/blob/master/test/hook.async.js#L95 https://github.com/visionmedia/superagent/blob/master/test/node/not-modified.js#L31

These two are listed as exemplar test suites on http://visionmedia.github.io/mocha/ and I didn't spend much time looking for issues.

syrnick avatar Aug 26 '14 04:08 syrnick

I'll reopen this. I think it'd be helpful. While there are ways to determine cross-test deps w/o tooling, if we can automate that, it'd save people time.

After toying with this a bit, it appears non-trivial due to the hierarchical nature of Suites. Tests are run by recursing into Suites. To run Tests randomly, we'd have to enumerate them, randomize them, then work backwards.

This would cause before() and after() Hooks to be somewhat meaningless as they would get executed n times per n tests in a Suite (or rather, in the worst case, but only if we're careful), as we continually change contexts. Sounds like it'll incur a performance penalty.

Using random seeds and reporting auto-generated seeds seems trivial, however, reporters may need to know about this information, so that requires implementation(s) in the reporters.

Of course, I'm assuming what I've described here is what's being asked for. A feature like this needs a specification.

Other options include "randomize Suites" or "randomize tests within Suites" or some combination of the two. Practically speaking, this means that once you're in a describe() block A, you cannot execute tests in any parent or sibling describe() block B until all of the tests in A have been run (which looks to be a much more straightforward implementation, and won't cause hinkiness with before()/after()).

boneskull avatar Aug 26 '14 05:08 boneskull

What I am (and I think others are) asking for is the simplest of the options:

  • randomize the tests at the lowest level: within a single describe block; shuffle "it" statements.
  • randomize the order of top level suites (or randomize the order of files that get loaded)

I don't think there's much value in shuffling things at the intermediate levels.

Certainly a hack, but works for the lowest level https://github.com/syrnick/mocha/compare/random_order?expand=1&w=0

mocha - fail
connect - pass
superagent - fail
express - pass** 
websocket.io - pass (can't tell for sure)

** I got 2 intermittent failures out of 100 runs of the whole test suite either way.

syrnick avatar Aug 26 '14 05:08 syrnick

OK, that's certainly eaiser to implement!

I was looking at the seedrandom lib for this; use the pass option.

Would accept PR.

boneskull avatar Aug 26 '14 06:08 boneskull

I'll likely clean up that code and adjust the test suite over the next few days. Is underscore too heavy of a dependency for this? I could likely use something light like this: http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript.

syrnick avatar Aug 26 '14 17:08 syrnick

@boneskull I support your decision to reopen this. :+1:

Other options include "randomize Suites" or "randomize tests within Suites" or some combination of the two.

Seems more than good enough for me. No need to recurse all the way down, enumerate and shuffle.

jbnicolai avatar Aug 26 '14 20:08 jbnicolai

Great to hear this is going in.

I wonder if rspec has handled the recursive shuffle? Might be worth looking at their code?

On Tuesday, 26 August 2014, Joshua Appelman [email protected] wrote:

@boneskull https://github.com/boneskull I support your decision to reopen this. [image: :+1:]

Other options include "randomize Suites" or "randomize tests within Suites" or some combination of the two.

Seems more than good enough for me. No need to recurse all the way down, enumerate and shuffle.

— Reply to this email directly or view it on GitHub https://github.com/visionmedia/mocha/issues/902#issuecomment-53482124.

timruffles avatar Aug 26 '14 20:08 timruffles

@syrnick I would not want to accept a PR with such a large dependency, and instead use seedrandom. Without it, I'm not sure how you're going to support seeding. seedrandom allows you to specify a seed or not, and if you don't, it will return a seed to you. Then we could display it to the user and allow them to specify it, a la RSpec.

boneskull avatar Aug 27 '14 03:08 boneskull

@syrnick Mind you that if you do generate seeds, they may not be "displayable" without passing them off to reporters. I'm not very familiar with the reporting architecture, so I couldn't tell you for sure, or what to do...

boneskull avatar Aug 27 '14 03:08 boneskull

+1

saks avatar Sep 07 '14 06:09 saks

I haven't looked at the implementation, but +1 to randomly-ordered test execution by default being super important.

schoblaska avatar Sep 09 '14 18:09 schoblaska

@syrnick Please let me know if you intend to do this, thanks.

boneskull avatar Sep 13 '14 00:09 boneskull

I'm happy to do that, but I don't have an immediate ETA.

syrnick avatar Sep 16 '14 19:09 syrnick

:+1:, you guys still need help with a PR?

parkr avatar Nov 21 '14 17:11 parkr

Indeed, no one seems to have started working on this.

dasilvacontin avatar Nov 21 '14 17:11 dasilvacontin

First, it looks like a Fisher-Yates shuffle would do the job here.

Second, I'd rather have --order random, --order random-suites, and --order default as the three arguments, with an optional :<seed>.

TimothyGu avatar Jan 19 '15 20:01 TimothyGu

+1. Just found a bug that would've shown up a long time ago if tests were randomized. Similar to how RSpec supports it.

demisx avatar Jan 30 '15 21:01 demisx

Here's some code that illustrates the usefulness of random test ordering. While there are simpler examples, this is one I just encountered during a TDD demo. If you reverse the order of the tests, the first test always fails.

game.js:

var express = require('express');
app = exports.app = express();

var sum = 0;

app.post('/bowl/:pins', function(req,res) {
    var score = parseInt(req.params.pins);
    console.log('Bowled ' + score);
    sum += parseInt(req.params.pins);
});

app.get('/score', function(req,res) {
    console.log('Sum: ' + sum);
    res.send(sum + '');
});

app.listen(process.env.PORT || 3000);

test\gameTest.js:

var request = require('supertest'),
    should = require('should'),
    game = require('../game.js').app;

describe('a game of bowling', function() {
    describe('a gutter game', function() {
        it('should score 0', function(done){
            request(game).get('/score').expect(200, '0', done);
        });
    });

    describe('a single pin game', function() {
        it('should score 20', function(done){
            for(var i = 0; i < 20; i++) {
                request(game).post('/bowl/1').expect(200, done);
            }
            request(game).get('/score').expect(200, '20', done); 
        });
    });
});

neontapir avatar Mar 10 '15 16:03 neontapir

I would love to have this.

elicwhite avatar Apr 02 '15 17:04 elicwhite

:+1:

tomconroy avatar Apr 30 '15 16:04 tomconroy

Once you get a few globals involved (this is Javascript, remember), start stubbing out server calls, and inserting/removing things from the DOM in your tests, it is very easy to add order-dependency. Randomizing the test order would help discover these earlier rather than later. :+1:

fishermand46 avatar Jun 03 '15 17:06 fishermand46

:+1:

tsiege avatar Jun 19 '15 21:06 tsiege

:+1:

jvennix-rmn avatar Jul 29 '15 20:07 jvennix-rmn

+1

baio avatar Aug 14 '15 16:08 baio

Random order by default, with optional seed to recreate ordering, would be a great feature to have.

alxndr avatar Oct 08 '15 16:10 alxndr

+1 to have it, my tests sometime fail when run in random order...

In the mean time, unix to the rescue (Unfortunately, random seed no supported):

mocha `ls -1 test/*.js | sort --random-sort `

NicolasJacob avatar Oct 22 '15 13:10 NicolasJacob

Was googling for what order mocha runs tests in and found this. In absence of randomization, what is the default run order? Is it always the order in which the tests appear physically in the file?

danielabar avatar Oct 29 '15 21:10 danielabar

:+1:

jmnsf avatar Nov 10 '15 09:11 jmnsf

@danielabar yeah they'll be in-order they appear in the file.

travisjeffery avatar Nov 10 '15 09:11 travisjeffery

@NicolasJacob well, random seed is actually possible to some extent, btw. :)

$ seq 10 | shuf --random-source=<(yes 2883)
1
7
3
4
6
2
10
5
9
8

ryoqun avatar Nov 12 '15 17:11 ryoqun

https://github.com/bahmutov/rocha works for this.

macalinao avatar Dec 27 '15 06:12 macalinao

@boneskull whilst this is an old issue, is the PR Please label still valid? If so I'll get something contributed in the next day or so.

jgwmaxwell avatar Mar 07 '16 17:03 jgwmaxwell

I think in the pursuit of eventually trying to keep the mocha core minimal, the team might be hesitant in introducing many new features. The next major release of mocha has the goal of having a pluggable interface.

Might I suggest just using https://github.com/bahmutov/rocha if it works?

danielstjules avatar Mar 09 '16 03:03 danielstjules

Awesome sauce

macalinao avatar Mar 09 '16 06:03 macalinao

What do you mean by pluggable interface? Will it be possible to introduce a randomized testing order via this interface?

mren avatar Mar 10 '16 14:03 mren

+1 for the feature request

sulabhjain avatar May 03 '16 18:05 sulabhjain

@sulabhjain, previous and following supporters, please use the +1 reaction instead.

dasilvacontin avatar May 03 '16 19:05 dasilvacontin

Progress in this branch.

boneskull avatar Jun 21 '16 04:06 boneskull

+1 for this feature

timlesallen avatar Jun 30 '16 03:06 timlesallen

This is really one the most critical features for a testing framework to help keep tests independent. Every major JVM test framework has this basic feature.

memelet avatar Jul 26 '16 04:07 memelet

+1 for this feature. Yes, it is easy to avoid test dependencies with enough experience and/or working alone, but this is not always the case.

aliakb avatar Nov 15 '16 18:11 aliakb

For those interested in this feature, they can send PRs against the randomization branch to help finish what's left.

boneskull avatar Nov 20 '16 19:11 boneskull

+1 for the feature. Really appreciate that a branch is in progress for this.

rserur avatar Nov 30 '16 15:11 rserur

Still waiting on this :))

jekku avatar Jan 04 '17 07:01 jekku

This can be really useful. @tj I understand that is easy to avoid test dependencies when you work with people with some basic skills about testing, but sometimes you need to take over a development team and you can bump with people with not even basic knowledge about test cases.

In fact, this is also useful when you take over existent projects and want to easily check if one test is tied to the previos one.

martincad avatar Feb 06 '17 15:02 martincad

@boneskull Great work! What's the status of this fix? Do you need help in anything?

krzkaczor avatar Mar 09 '17 11:03 krzkaczor

I just wanted to share my temporary solution that I use for running mocha tests in random order. Maybe it will be useful for someone.

mocha $(find tests/ -name *.spec.js | shuf)

kmarkow avatar Aug 26 '17 17:08 kmarkow

Unfortunately that doesn't shuffle test examples within the same example, but that's still pretty clever and handy!

nickserv avatar Aug 26 '17 17:08 nickserv

+1 in support of this feature

ccurtisj avatar Sep 23 '17 20:09 ccurtisj

This is still on the table, but needs attention from not-me

boneskull avatar Oct 17 '17 04:10 boneskull

So, what's actually left here? Where can I start?

elsbrock avatar Dec 14 '17 12:12 elsbrock

Would love to see that implemented ❤️

caiogondim avatar Dec 21 '17 00:12 caiogondim

I just found the choma package, which provides a very simple plugin for Mocha to randomize the order of test suites and cases. Good alternative to rocha, which was mentioned earlier. Simple and solves the problem for me!

jhecking avatar Jan 10 '18 08:01 jhecking

An alternative would be to run tests in parallel:

Glogo avatar Jan 24 '18 10:01 Glogo

8 years from the original message, nice 😃

unickq avatar May 18 '21 21:05 unickq

I have moved on to jest

pke avatar May 19 '21 08:05 pke