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

🐛 Bug: Mocha throws error if it is running a spec more than once #2706

Open
crishushu opened this issue Feb 8, 2017 · 21 comments
Open

🐛 Bug: Mocha throws error if it is running a spec more than once #2706

crishushu opened this issue Feb 8, 2017 · 21 comments
Labels
status: accepting prs Mocha can use your help with this one! type: bug a defect, confirmed by a maintainer

Comments

@crishushu
Copy link

taken from https://cdnjs.com/libraries/mocha

> mocha.run();

everything ok!

> mocha.run()

TypeError: Cannot read property 'call' of undefined
    at r (https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.min.js:2:8563)
    at r.run (https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.min.js:2:9635)
    at n (https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.min.js:2:13371)
    at https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.min.js:2:13710
    at i (https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.min.js:1:572)
@dennisbaskin
Copy link

Do you have an example of what necessitated multiple programatic runs of mocha?

@tgirishkumar
Copy link

I have the same issue:
We have a test framework that runs tests written in mocha after doing some device setup.
We earlier had a repeat feature where we called mocha.run n times if that feature was enabled.

We haven't tested this feature from long time but realized today that it is broken and we get same error as @crishushu mentioned.

Please confirm if Mocha supports repeating tests which can be controlled as an argument?
I am aware of function within it/describe.

thanks

@dennisbaskin
Copy link

It is really hard to judge what this could be without a code example, including a setup. Out of curiosity have you looked https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically ?

The wiki describes that the run method returns a Runnable, which might help you debug. For example: try running your second test after the first has completed and see if that helps:

Mocha.run().on('test end', runAgainFunc);

@tgirishkumar

Please confirm if Mocha supports repeating tests which can be controlled as an argument?
I am aware of function within it/describe.

I am not sure what you mean here, could you elaborate?

@tgirishkumar
Copy link

I meant the retry feature:
https://mochajs.org/#retry-tests , we are aware of it but wanted to retry/repeat tests without code changes.

thanks

@tgirishkumar
Copy link

Here is another example of someone raising the issue:

adamgruber/mochawesome#79, Look at last comment:

Please note: this error happens in mochawesome but caused by this issue in Mocha inside, i tested by reproducing below issue and then remove mochawesome and then i find current issue.


Here's my code:


import * as path from 'path';
import * as fs from 'fs';
import * as Mocha from 'mocha';
import * as React from 'react';
import * as chokidar from 'chokidar';

let mocha: Mocha = new Mocha(
{
    reporter: 'mochawesome',
    reporterOptions:
    {
        reportDir: __dirname + '/../../public/mochawesome'
    }
});

let testsPath: string = __dirname + '/../../tests/';
let fileList: Array<string> = fs.readdirSync(testsPath).filter(file => file.split('.').pop() == 'js');
fileList.forEach (file =>
{
    console.log(file);
    mocha.addFile(path.join(testsPath, file));
});

function Invalidate()
{
    fileList.forEach (p =>
    {
        let fullPath = path.join(testsPath, p);
        p = path.resolve(fullPath);
        delete require.cache[require.resolve(p)];
    });
}

export default class Tests extends React.Component<{}, {}>
{
    componentWillMount()
    {
        //mocha.run();

        let watcher = chokidar.watch(testsPath, {persistent: true});
        watcher.on('change', path =>
        {
            console.log('change');
            Invalidate();
            mocha.run();
            //this.forceUpdate();
        });

        watcher.on('add', p =>
        {
            if (p.split('.').pop() == 'js')
            {
                console.log('add');
                Invalidate();
                mocha.addFile(p);
                mocha.run();
                //this.forceUpdate();
            }
        });
    }

    render()
    {

        return (
            <html>
                <body>
                    <iframe src='/mocha/mochawesome.html' width='100%' height='100%'/>
                </body>
            </html>
        );
    }
}

And my test code:

"use strict";
const chai_1 = require("chai");
describe('testing', function () {
    it('will equal 1', () => {
        chai_1.expect(1).to.equal(2);
    });
});

When the add or watch event is called more than once, it fails.```

@elevow
Copy link

elevow commented Feb 15, 2017

Using 3.2.0 as well * edit: same behavior with 3.1.2
I am getting the same error and I am running mocha programatically already. My dept has a shared web component with multiple versions and we are testing that we did not break any version as prior versions of the component could be used by other teams. So we run the same tests on multiple versions of a component. As a side note, we use a SemVer check when new tests are added or need to be skipped.

The error seems to occur on the first line of code on the second time the file is run because I cannot get anything including logs. There are no problems or errors when I attempt to run only 1 version (so one run of the tests).

I thought maybe if I didn't use addFile multiple times that would work, but it did not make a difference.

Just a guess, I was wondering if the test is being cached like a require then would it need to be "unregistered" so it can be run again with new information?

Here is a brief output from my tests:
global-nav-module_5.0.0 Component Tests Suite1
√ module should be added and render with option parameters true (12341ms)

1 passing (12s)

  • Second Run - I noticed that the 5.0.0 (is a var) that is not being updated to display 5.0.1 *
    global-nav-module_5.0.0 Component Tests
    global-nav-module_5.0.0 Component Tests Suite1
    1) "before all" hook

    0 passing (3ms)
    1 failing

    1. global-nav-module_5.0.0 Component Tests global-nav-module_5.0.0 Component Tests Suite1 "before all" hook:
      TypeError: Cannot read property 'call' of undefined

@dennisbaskin
Copy link

@tgirishkumar thank you for clarifying. I have never used mocha for React tests, nor have had a need for using retries on failed tests. I have generally setup tests to always run in one command (mocha <....>) from the command line. I would imagine it's easier to setup CI scripts that way, but have no way of comparing since I have never tried to do it the other way.

It is possible mocha is relying on something in the global state or loading setup multiple times. Hard to trace without a reproducible unit test / stack trace. Maybe with @elevow and @crishushu we can create a reproducible small unit test, track down the issue, and then submit a PR for the mocha team. They mentioned on their site that they are in dire need of help maintaining the project 😄

@crishushu
Copy link
Author

Yesterday I was playing with different versions of mocha. AFAIK the latest release where running mocha with same suite/spec setting multiple times is supported, is 2.3.0. @dennisbaskin just out of curiosity is there any reason why the feature (yes, I consider it as one) has been disabled. I assume that it happened deliberately.

@elevow
Copy link

elevow commented Feb 16, 2017

I was able to make a small version of the failure. Hopefully someone can duplicate it. one.test.js is just a it statement with console.log();

const Mocha = require('mocha');
const path = require('path');
const async = require('async');

const mocha = new Mocha({
  reporter: 'spec',
});
const counter = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

const cwd = process.cwd();

mocha.addFile(path.join(cwd, 'one.test.js'));
async.eachLimit(counter, 1, (iCounter, eachCallback) => {
  mocha.run((failures) => {
    eachCallback();
  });
});

TypeError: Cannot read property 'call' of undefined

edit: I tried moving the addFile command inside and outside of the eachLimit and both give the failure

@elevow
Copy link

elevow commented Feb 16, 2017

Hey after some testing the issue seems to be specific to async.eachLimit for me.

@dennisbaskin
Copy link

@crishushu I would not able to answer your question as to why. I am not directly involved on the contributing team, just trying to help triage issu.es since they need help.

@elevow I will try to play around with your example, thanks for setting that up.

@drazisil drazisil added type: bug a defect, confirmed by a maintainer unconfirmed labels Mar 30, 2017
@qamate
Copy link

qamate commented Jul 11, 2017

Guys, any update on this?

@tomdid
Copy link

tomdid commented Aug 11, 2017

I have same problem. I just tried to set up mocha programmatically and have just one test is expect(1).toBe(1) . No fancy stuff, like react or enzyme is loaded.

I load one test file
mocha.add('my.test.js')

then i run the tests.

mocha.run();
setTimeout(()=>{ mocha.run(); }, 5000);

First time it runs fine, after 5s it throws me an error. This is the full error stack
TypeError: Cannot read property 'call' of undefined at callFn ([__dirname]\node_modules\mocha\lib\runnable.js:348:20) at Test.Runnable.run ([__dirname]\node_modules\mocha\lib\runnable.js:340:7) at Runner.runTest ([__dirname]\node_modules\mocha\lib\runner.js:443:10) at [__dirname]\node_modules\mocha\lib\runner.js:549:12 at next ([__dirname]\node_modules\mocha\lib\runner.js:361:14) at [__dirname]\node_modules\mocha\lib\runner.js:371:7 at next ([__dirname]\node_modules\mocha\lib\runner.js:295:14) at Immediate.<anonymous> ([__dirname]\node_modules\mocha\lib\runner.js:339:5) at runCallback (timers.js:800:20) at tryOnImmediate (timers.js:762:5)

@qamate
Copy link

qamate commented Aug 24, 2017

I think you should have a look at grunt-loop-mocha.

I use it for my Selenium WebDriver tests.

https://github.com/grawk/grunt-loop-mocha

My understanding is that mocha is a unit testing framework and so it creates only one instance/process of your tests. So, when you try to execute the same test, it does not execute before and after statements.

Grunt-loop-mocha on other hand helps you create the new instance for each specification. So, it will even execute before and after statements with the different configuration. I guess that's what you are looking for.

@karneaud
Copy link

karneaud commented Oct 9, 2017

+1

mocha --compilers js:babel-register --allow-uncaught --full-trace --no-warnings --recursive tests/e2e-tests

got this error

Frontend Testing "before all" hook: open browser:
TypeError: Cannot read property 'call' of undefined
at callFn (/usr/src/app/node_modules/mocha/lib/runnable.js:348:20)
at Hook.Runnable.run (/usr/src/app/node_modules/mocha/lib/runnable.js:340:7)
at next (/usr/src/app/node_modules/mocha/lib/runner.js:309:10)
at Immediate. (/usr/src/app/node_modules/mocha/lib/runner.js:339:5)
at runCallback (timers.js:781:20)
at tryOnImmediate (timers.js:743:5)
at processImmediate [as _immediateCallback] (timers.js:714:5)

Frontend Testing "after all" hook: quit browser:
TypeError: Cannot read property 'call' of undefined
at callFn (/usr/src/app/node_modules/mocha/lib/runnable.js:348:20)
at Hook.Runnable.run (/usr/src/app/node_modules/mocha/lib/runnable.js:340:7)
at next (/usr/src/app/node_modules/mocha/lib/runner.js:309:10)
at Immediate. (/usr/src/app/node_modules/mocha/lib/runner.js:339:5)
at runCallback (timers.js:781:20)
at tryOnImmediate (timers.js:743:5)
at processImmediate [as _immediateCallback] (timers.js:714:5)

my test

import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import webdriver from 'selenium-webdriver';
import proxy from 'selenium-webdriver/proxy';
import chrome from 'selenium-webdriver/chrome';
// Helper objects for performing actions.
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';

// We're going to use the ShopPage and CartPage objects for this tutorial.
import { ShopPage, CartPage } from 'wc-e2e-page-objects';

chai.use( chaiAsPromised );
const assert = chai.assert;
const chromedriver = require('chromedriver');
const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {args: ['--headless --no-sandbox user-agent=Mozilla/5.0 (wp-e2e-tests) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36' ]});
const pref = new webdriver.logging.Preferences();
pref.setLevel( 'browser', webdriver.logging.Level.SEVERE );

let manager;
let driver;

test.describe( 'Frontend Testing', function() {

    // Set up the driver and manager before testing starts.
    test.before( 'open browser', function() {
        driver = new webdriver.Builder()
          .forBrowser('chrome')
          .usingServer( config.get("url") )
          .withCapabilities(chromeCapabilities)
    		.setLoggingPrefs(pref)
    		.setProxy(proxy.direct())
          .build();
        driver.getSession().then( function( sessionid ) {
			driver.allPassed = true;
			driver.sessionID = sessionid.id_;
		} );

        helper.clearCookiesAndDeleteLocalStorage( driver );
    } );
    // Tests will go here.
    test.it( 'Has shop page', () => {

            // Create a new Shop page object.
            const shopPage = new ShopPage( driver, { url: config.get('url').concat( '/shop' ) } );

            assert.instanceOf(shopPage, ShopPage, 'shopPage instance of ShopPage');

        } );
    // Close the browser after finished testing.
    test.after( 'quit browser', () => {
        driver.sleep( config.get('startBrowserTimeoutMs') ).then( () => {
			return driver.quit();
		} );
    } );

} );

I'm running in a docker environment with

node 8.5.0
npm 5.4.2
mocha 3.5.3

@super9user
Copy link

super9user commented Apr 20, 2018

This worked for me, if anyone is still looking for a solution

const purge = function (allFiles) {
  allFiles.forEach(file => {
    delete require.cache[file];
  });
};
// use this function when rerunning tests
const rerun = function () {
  purge(allFiles);
  mocha.suite = mocha.suite.clone();
  mocha.suite.ctx = new Mocha.Context();
  mocha.ui("bdd");
  mocha.files = allFiles;
  mocha.run()
};

@kraenhansen
Copy link

kraenhansen commented Jul 21, 2018

I am seeing this issue too using Mocha 5.2.0 - here is my stacktrace:

TypeError: Cannot read property 'call' of undefined
  at callFn (/.../node_modules/mocha/lib/runnable.js:372:21)
  at Test.Runnable.run (/.../node_modules/mocha/lib/runnable.js:364:7)
  at Runner.runTest (/.../node_modules/mocha/lib/runner.js:455:10)
  at /.../node_modules/mocha/lib/runner.js:573:12
  at next (/.../node_modules/mocha/lib/runner.js:369:14)
  at /.../node_modules/mocha/lib/runner.js:379:7
  at next (/.../node_modules/mocha/lib/runner.js:303:14)
  at Immediate.<anonymous> (/...node_modules/mocha/lib/runner.js:347:5)
  at runCallback (timers.js:794:20)
  at tryOnImmediate (timers.js:752:5)
  at processImmediate [as _immediateCallback] (timers.js:729:5)

I am writing a library where I want to compare the output to console of a reporter based with and without my library .. which is why I need to be able to re-run the same test twice, programatically.

When inspecting the Mocha.Test objects I see that they are missing a fn property the second time they run, which probably is why runnable.js:372 is calling an undefined function.

It looks like a Mocha.Suite is cleaning up by deleting these fn properties to avoid memory leaks https://github.com/mochajs/mocha/blob/master/lib/runner.js#L817-L819.

I ultimately gave up and just reinstantiated the mocha instance between runs - remembering to clear node.js require cache (delete require.cache[testPath]) between runs.

@RyanLiuF
Copy link

I clean the require cache up by using the clear-require component,
and it works every time, the code as follows:
const clearRequire = require('clear-require');
clearRequire.match(new RegExp('mocha'));//must be here
clearRequire.match(new RegExp('your-test-js-file'));//must be here
more details :
const clearRequire = require('clear-require');
function testMochaClick()
{
clearRequire.match(new RegExp('mocha'));
clearRequire.match(new RegExp('commtest'));
let Mocha = require('mocha');
let mocha = new Mocha({
reporter: 'mochawesome',
reporterOptions: {
reportDir: './resources/app/report',
}
})
mocha.addFile('./resources/app/case/commtest.js');
mocha.run();
}
https://mochajs.org/api/mocha#loadFiles

@plroebuck
Copy link
Contributor

The current codebase needs mocha to be reinstantiated between runs due to internal implementation issues.

@JoshuaKGoldberg
Copy link
Member

Related: #1938

@JoshuaKGoldberg JoshuaKGoldberg changed the title mocha 3.2.0 throws error if it is running a spec more than once 🐛 Bug: mocha throws error if it is running a spec more than once Dec 27, 2023
@JoshuaKGoldberg JoshuaKGoldberg changed the title 🐛 Bug: mocha throws error if it is running a spec more than once 🐛 Bug: Mocha throws error if it is running a spec more than once Dec 27, 2023
@JoshuaKGoldberg
Copy link
Member

Looking back at this issue and #1938: fixing it would be a pretty significant internal refactor. I think we can accept PRs for it, but note that per #5027 it'll be a while before we can review them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: accepting prs Mocha can use your help with this one! type: bug a defect, confirmed by a maintainer
Projects
None yet
Development

No branches or pull requests