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

The same test suite won't run twice when using mocha programmatically #995

Closed
selaux opened this issue Oct 7, 2013 · 27 comments
Closed

Comments

@selaux
Copy link

selaux commented Oct 7, 2013

When trying to execute a test suite twice, using the programmatic approach, the second time it should be executed it isn't found. This is especially annoying when trying to execute the same test with differing configuration.

Minimal Example (the test/test.js file contains a suite with a single always-passing test):

var Mocha = require('mocha');

function runMocha(callback) {
    var mocha = new Mocha();

    mocha.addFile('test/test.js');

    mocha.run(function () {
        callback();
    });
}

runMocha(function () {
    runMocha(function () {});
});

The first time mocha is run it will execute the test, the second time it will not.

The cause for this is that the second time it is required in mocha.js, the file is already cached and won't be executed. The only solution I was able to find is deleting the cached file from require.cache, but that doesn't look like good practice to me at all.

@isacssouza
Copy link

I'm also facing this issue.

@nomilous
Copy link

nomilous commented Nov 4, 2013

Move the new Mocha() outside runMocha - to only create the instance once.

@travisjeffery
Copy link
Contributor

yep, you can do:

var Mocha = require('mocha');

var mocha = new Mocha();
mocha.addFile('test.js');

function runMocha(callback) {
  mocha.run(function () {
    callback();
  });
}

runMocha(function () {
  runMocha(function () {});
});

@selaux
Copy link
Author

selaux commented Nov 4, 2013

Yes it works for this simple example, but what if I don't have control over when mocha is instantiated (i.e. when using a grunt plugin) or i only want to execute a subset of the tests?

BTW: In your example I would expect the tests in 'test.js' to run twice on the second run, since I called addFile twice (and it will be contained twice in this.files in mocha.)

@travisjeffery
Copy link
Contributor

both of those are separate from this.

for running a subset of tests in a file you wouldn't do anything different than you'd do anyway i.e. either use describe, it.skip or something.

for the grunt thing, post a real example.

@blented
Copy link

blented commented Mar 26, 2014

Anyone ever solve this issue? I have the same problem as the OP, can't run a test twice, it runs once and then on the second run does nothing.

@mkleehammer
Copy link

It appears to be because the tests have a state attribute added. Is there a proper way to "reset" tests? I'm using the following which works:

function resetTests(suite) {
  suite.tests.forEach(function(t) {
    delete t.state;
    t.timedOut = false;
  });
  suite.suites.forEach(resetTests);
}

resetTests(mocha.suite);
mocha.run();

@sunrize531
Copy link

Just added real example for "grunt thing", but I think this should be fixed on grunt plugin side. So the issue in the plugin repository.

@Victorystick
Copy link

I just realised that mocha supports a --watch mode, which means that it must clear the require cache whenever a file is changed. Is this the kind of hackery we must resort to to make it work?

@dlgltlzed
Copy link

How do I run tests more than once?

var Mocha = require('mocha');
var mocha = new Mocha({});
mocha.addFile('test.js');
mocha.run();

setTimeout(function() {
    mocha.run();
},1000);

Output is something like:

  suite
    √ test


  1 passing (11ms)



  suite
    1) test


  0 passing (16ms)
  1 failing

  1) suite test:
     TypeError: Cannot read property 'call' of undefined
      at callFn (node_modules\mocha\lib\runnable.js:334:20)
      at Test.Runnable.run (node_modules\mocha\lib\runnable.js:327:7)
      at Runner.runTest (node_modules\mocha\lib\runner.js:429:10)
      at node_modules\mocha\lib\runner.js:535:12
      at next (node_modules\mocha\lib\runner.js:349:14)
      at node_modules\mocha\lib\runner.js:359:7
      at next (node_modules\mocha\lib\runner.js:285:14)
      at Immediate.<anonymous> (node_modules\mocha\lib\runner.js:327:5)

@sunrize531
Copy link

Same issue, basically.

@CallMeBW
Copy link

Any updates on what @moglars pointed out?
I also face this issue while running the tests twice in the same process. This github issue should not be reopened.

@traviswimer
Copy link

traviswimer commented Nov 20, 2016

So I definitely had to solve this problem at least once before, yet tonight I could not remember how, so here is an example to assist my future self (or anyone else 😃 ):

function runMochaTests() {
    Object.keys( require.cache ).forEach( function( file ) {
        delete require.cache[ file ];
    } );

    var mocha = new Mocha();

    mocha.addFile( 'run_test.js' );

    mocha.run();
}

Its ugly, but you should be able to call runMochaTests() multiple times.

wluu pushed a commit to appcelerator-archive/appium-tests that referenced this issue Jan 20, 2017
tcoulter added a commit to trufflesuite/truffle that referenced this issue Mar 20, 2017
@aDu
Copy link

aDu commented May 3, 2017

@traviswimer the problem with your solution is that it seems to reset the local module variables in the required modules (i.e. it clears the require cache - which I don't want to happen). Although, your solution does work, it seems to have the side effects.

@ruthnaebeck
Copy link

ruthnaebeck commented May 13, 2017

If anyone comes across this post and is trying to run mocha tests in your browser, here is what I did to reset the tests after mocha.run()

const resetTests = () => {
  mocha.suite.suites = []
  let testSpecs = document.getElementById('testSpecs')
  if (testSpecs) testSpecs.remove()
  testSpecs = document.createElement('script')
  testSpecs.src = '/questions-specs/testSpecs.js'
  testSpecs.async = true
  testSpecs.id = 'testSpecs'
  document.body.appendChild(testSpecs)
}

setTimeout(resetTests, 1000)

@MarcelGeo
Copy link

Hello.

The solution from @traviswimer is not working. My mocha tests are running only once. I can not debug it. I dont now solution.

@yarivat
Copy link

yarivat commented Aug 3, 2017

@MarcelGeo did u find the solution here

@NeethuKP
Copy link

NeethuKP commented Oct 4, 2017

I am facing a similar issue and the solution suggested to delete the cache is not working.

@PigBoT
Copy link

PigBoT commented Oct 23, 2017

I almost lost all of my hair. The issue is stated at the bottom of the 'angelo' npm module description, https://www.npmjs.com/package/angelo.

The test files are being loaded with a require, which is being cached. A simple fix would be to remove the test files cached after each run. I wouldn't consider this hacky.

For my own quick workaround, I'm just calling delete require.cache[testFileName] before addFile(testFileName).

@jcjolley
Copy link

I am also experiencing this issue!

@ORESoftware
Copy link

You should execute mocha.run() in a child process...create your programmatic implementation in a parent process, every time you want to re-run tests, spawn a new child process

@jcjolley
Copy link

jcjolley commented Oct 25, 2017

Deleting the require.cache did not work for me. Spawning a child process did. For my use case, here was the before and after:
BEOFRE:

const testFile = (filename, reporter) => {
    if (filename.includes('.test.js')) {
        const output = [];
        const mocha = new Mocha({ reporter: reporter });
        delete require.cache[filename]
        mocha.addFile(filename);

        return new Promise((resolve, reject) => {
            mocha.run(failures => {
                resolve(failures);
            })
        })
    }
};

AFTER:

const spawn = require('child-process-promise').spawn;

const testFile = (filename, reporter) => {
    if (filename.includes('.test.js')) {
        const output = [];
        const promise = spawn('mocha', ['-R', reporter, filename], {stdio: "inherit"})

        return new Promise((resolve, reject) => {
            promise
                .then(() => resolve(0))
                .catch((err) => {resolve(1) })
        })
    }
};

@ddimitrioglo
Copy link

ddimitrioglo commented Jan 19, 2018

Faced the same issue, and options above have not worked for me.
I just created a helper MochaRunner class and now I can rerun the same tests.

// Usage
const runner1 = new MochaRunner();
const runner2 = new MochaRunner();

runner1.run(['test1.js', 'test2.js']).then(() => {
   runner1.cleanup();
});

runner2.run(['test1.js']).then(() => {
   runner2.cleanup();
})

and in both cases test1.js will be run!

P.S. Hope it will help to someone 😉

@colinbendell
Copy link

Deleting the cache right after running require() seems to do nicely. I suspect it's very racy but I've been able to run the test hundreds of time in parallel.

let mocha = new Mocha({});

mocha.suite.on('require', function (global, file) {
    delete require.cache[file];
});

//TODO: add files to suite & do things

mocha.run();

@gabssnake
Copy link

If anyone is getting node warnings about EventListener leaks on process uncaughtException,
be sure to call mocha.dispose() on every Mocha instance you create. See #2783

@3wweiweiwu
Copy link

3wweiweiwu commented Jan 26, 2022

In case anyone interest, I find another work around to resolve this problem. It's inspired by @colinbendell . Here is my solution:

      this.__mocha = new Mocha({ timeout: timeout })
        this.__mocha.suite.on('require', function (global, file) {
            delete require.cache[file];
            require(file)
        });

Things I tried but didn't work in 9.2.0

        this.__mocha = new Mocha({ timeout: timeout })
        this.__mocha.suite.on('require', function (global, file) {
            delete require.cache[file];
        });
            this.__mocha.addFile(this.__filePath)
            // this.__mocha.cleanReferencesAfterRun(false)
            await this.__mocha.loadFilesAsync()

            let runner = this.__mocha.run(() => {
                this.__mocha.dispose()
            })

@ArquintL
Copy link

ArquintL commented Sep 1, 2022

If anyone is getting node warnings about EventListener leaks on process uncaughtException,
be sure to call mocha.dispose() on every Mocha instance you create. See #2783

Calling mocha.dispose() did the trick for me to sequentially execute tests in different mocha instances

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests