214

I have a code where certain tests will always fail in CI environment. I would like to disable them based on an environment condition.

How to programmatically skip a test in mocha during the runtime execution?

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 3
    Programmatically skipping a test is covered by `this.skip()` in https://mochajs.org/#inclusive-tests and @zatziky's answer below. The rest of the answers are obsolete for Mocha v3+ – Patrick May 29 '17 at 13:16
  • 1
    describe.skip('description', () => {}) / describe.only('description', () => {}) / it.skip('description', () => {}) / it.only('description', () => {}) – Jun Feb 21 '18 at 10:16
  • If you're looking for a way to do this in TypeScript, see [this related StackOverflow answer](https://stackoverflow.com/questions/72970456/how-to-conditionally-skip-a-mocha-test-in-typescript/72970457). – user1742189 Jul 16 '22 at 04:54

17 Answers17

232

You can skip tests by placing an x in front of the describe or it block, or placing a .skip after it.

xit('should work', function (done) {});

describe.skip('features', function() {});

You can also run a single test by placing a .only on the test. for instance

describe('feature 1', function() {});
describe.only('feature 2', function() {});
describe('feature 3', function() {});

Only the feature 2 block would run in this case.

There doesn't appear to be a way to programmatically skip tests, but you could just do some sort of check in a beforeEach statement and only run the test if the flag was set.

beforeEach(function(){
    if (wrongEnvironment){
        runTest = false
    }
}

describe('feature', function(){
    if(runTest){
         it('should work', function(){
            // Test would not run or show up if runTest was false,
         }
    }
}
Gajus
  • 69,002
  • 70
  • 275
  • 438
KJ3
  • 5,168
  • 4
  • 33
  • 53
  • 11
    Your 2nd attempt at a solution won't work, because the order of execution is not the one you think. When the `beforeEach` call executes, Mocha *records* anonymous function (the "hook") for *future* use, when the `describe` call executes, Mocha *immediately* executes the anonymous function passed to it. So by the time `if (runTest)` is executed, the `beforeEach` *hook* won't have run. – Louis Apr 25 '16 at 11:03
  • 42
    How does this answer have 27 upvotes? The question asks about programmatically skipping tests, so adding ".skip" or ".only" is not helpful. Then it explicitly says you can't do what the OP wants to do, despite the fact that other answers tell you how to do it. – Graeme Perrow Sep 20 '16 at 14:47
  • @Louis your point is correct. I have same issue where my test run/skip info will be known only after beforeEach. Is there any way to achieve it? – Ponmudi VN Mar 28 '17 at 08:40
  • 4
    Won't work, not an answer to the question, see @Gajus' response instead – NorTicUs Jul 06 '17 at 09:02
  • 4
    This answer has merits for a different question that wasn't asked here. I don't have the power to change anything here. See the this.skip() answer. – Andrew Martinez Aug 13 '18 at 20:25
  • 4
    this does not answer the question – Ingo Renner Feb 22 '19 at 01:12
  • 1
    Downvote because this answer does not answer the question, and because the last chunk of code is incorrect. – RoboticRenaissance Aug 26 '20 at 16:56
160

Use Mocha's skip() function

It can be used to either statically to disable a test or entire suite, or dynamically skip it at runtime.

Here's an example runtime usage:

it('should only test in the correct environment', function() {
  if (/* check test environment */) {
    // make assertions
  } else {
    this.skip();
  }
});
Inigo
  • 12,186
  • 5
  • 41
  • 70
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 27
    Readers might like to know that this marks the entire `describe` as skipped (i.e. all the tests in the `describe` are skipped). – Louis Sep 28 '15 at 10:24
  • Mocha's "pending tests" documentation: https://mochajs.org/#pending-tests – lasec0203 Dec 19 '17 at 05:20
  • describe.skip('description', () => {}) / describe.only('description', () => {}) / it.skip('description', () => {}) / it.only('description', () => {}) – Jun Feb 21 '18 at 10:15
  • I dont underdtand why this kind of answer is upvoted. its a hack - and not a preety one. – chenop Aug 20 '19 at 03:39
  • 5
    actual documentation https://mochajs.org/#inclusive-tests , it's not a hack btw in any way, but correct method of excluding some tests based on runtime settings. i.e. it answers exactly what the original question asked. Thanks @xavdid – WowPress.host Oct 18 '19 at 16:14
  • @WowPress.host I didn't answer the question, just made a minor edit at some point :D – xavdid Oct 18 '19 at 19:23
  • 3
    If you're here and you're using **Jest**, hoping that it supports `this.skip()` in the same way, I'll save you some time - it doesn't. – Joshua Pinter Jun 07 '20 at 18:56
  • Doesn't work for async tests. – Catfish Dec 21 '22 at 18:37
67

This answer does work for ES6.

Instead of:

describe('your describe block', () => {

You want:

(condition ? describe : describe.skip)('your describe block', () => {

This conditionally skips all tests in the describe block IF the condition is false.

Or, instead of:

it('your it block', () => {

You want:

(condition ? it : it.skip)('your it block', () => {

This conditionally skips one test IF the condition is false.

xavdid
  • 5,092
  • 3
  • 20
  • 32
danday74
  • 52,471
  • 49
  • 232
  • 283
  • 6
    I get what you are suggesting but you first need to define a *contextual* describe like this : `const contextualDescribe = shouldAvoidTests ? describe.skip : describe` then you can use it : `contextualDescribe('your it block', () => {` – Ser Oct 18 '17 at 16:37
  • 3
    @Ser To get on a single line, I used something like this: `(condition ? describe : describe.skip)('your describe block', () => {` – joshden Dec 21 '17 at 16:44
  • 1
    How to do this async? I need to look up the skip condition based on a redis flag, which is an async operation (we store feature flags in redis). – Patrick Finnigan Mar 17 '18 at 19:13
  • been a while but ive had this kind of need before too, i think i just wrapped all the mocha stuff in a function that was called after the async callback completed - cant remember the exact details – danday74 Mar 17 '18 at 20:21
  • I used to use this technique but now it fails for me. try simply writing `(it)('my test', () => {})` – cyrf Oct 05 '19 at 18:38
  • This does technically answer the OPs question, the decision to skip or not skip IS made "during the runtime execution", but it's actually rarely what you would really want because that decision is actually made at require time, when the module is run, not when the tests are run (which is what @Gajus details in his answer detailing `.skip()`). – Michael Oct 26 '19 at 11:29
  • 1
    Using arrow functions with mocha is not recommended: https://mochajs.org/#arrow-functions Simply pass a `function` or `async function` and then you can call `this.skip()` as needed, potentially after performing some asynchronous operation to check if you need to run the test. – Jason Hoetger Sep 11 '20 at 18:20
  • I've just written a module that let you do this with a syntax that's easier on the eyes: https://www.npmjs.com/package/mocha-skip-if. Makes it easier to comment out the skip too. – cleong Nov 30 '20 at 07:38
  • 1
    Instead of `(condition ? it : it.skip)('your it block', () => {` you could simply write `condition && it('your it block', () => {`. Due to the short-circuiting of `&&`, the it block won’t even be evaluated if `condition` is false. – chharvey Dec 19 '21 at 07:54
38

I use runtime skipping from Mocha for the same scenario as you're describing. It is the copy paste from the docs:

it('should only test in the correct environment', function() {
  if (/* check test environment */) return this.skip();

  // make assertions
});

As you can see, it skips the test based on environment. My own condition is if(process.env.NODE_ENV === 'continuous-integration').

Amio.io
  • 20,677
  • 15
  • 82
  • 117
  • 2
    Agreed! Can be a one liner by doing an early return perhaps? Like: `if (/* skipTestCondition */) return this.skip();` -- edit: works :D – SidOfc Aug 07 '19 at 20:53
25

to skip tests, use describe.skip or it.skip

describe('Array', function() {
  it.skip('#indexOf', function() {
    // ...
  });
});

to include tests you could use describe.only or it.only


describe('Array', function() {
  it.only('#indexOf', function() {
    // ...
  });
});

More info at https://mochajs.org/#inclusive-tests

random-forest-cat
  • 33,652
  • 11
  • 120
  • 99
  • 2
    the question was about how to skip the test only if some environment variable is set (or some other condition detectable at runtime) – Kip Jun 16 '22 at 16:13
7

It depends how you want to programmatically skip the test. If the conditions for skipping can be determined before any test code is run, then you can just call it or it.skip as needed, based on a condition. For instance, this will skip some tests if the environment variable ONE is set to any value:

var conditions = {
    "condition one": process.env["ONE"] !== undefined
    // There could be more conditions in this table...
};

describe("conditions that can be determined ahead of time", function () {
    function skip_if(condition, name, callback) {
        var fn = conditions[condition] ? it.skip: it;
        fn(name, callback);
    };

    skip_if("condition one", "test one", function () {
        throw new Error("skipped!");
    });

    // async.
    skip_if("condition one", "test one (async)", function (done) {
        throw new Error("skipped!");
    });

    skip_if("condition two", "test two", function () {
        console.log("test two!");
    });

});

If the conditions you want to check can only be determined at test time, it is a bit more complicated. If you do not want to access anything not strictly speaking part of the testing API, then you could do this:

describe("conditions that can be determined at test time", function () {
    var conditions = {};
    function skip_if(condition, name, callback) {
        if (callback.length) {
            it(name, function (done) {
                if (conditions[condition])
                    done();
                else
                    callback(done);
            });
        }
        else {
            it(name, function () {
                if (conditions[condition])
                    return;
                callback();
            });
        }
    };

    before(function () {
        conditions["condition one"] = true;
    });

    skip_if("condition one", "test one", function () {
        throw new Error("skipped!");
    });

    // async.
    skip_if("condition one", "test one (async)", function (done) {
        throw new Error("skipped!");
    });

    skip_if("condition two", "test two", function () {
        console.log("test two!");
    });

});

Whereas my first example was marking the tests as formally skipped (aka "pending"), the method I've just shown will just avoid performing the actual test but the tests won't be marked as formally skipped. They will be marked as passed. If you absolutely want to have them skipped I don't know of any way short of accessing parts that are not properly speaking part of the testing API:

describe("conditions that can be determined at test time", function () {
    var condition_to_test = {}; // A map from condition names to tests.
    function skip_if(condition, name, callback) {
        var test = it(name, callback);
        if (!condition_to_test[condition])
            condition_to_test[condition] = [];
        condition_to_test[condition].push(test);
    };

    before(function () {
        condition_to_test["condition one"].forEach(function (test) {
            test.pending = true; // Skip the test by marking it pending!
        });
    });

    skip_if("condition one", "test one", function () {
        throw new Error("skipped!");
    });

    // async.
    skip_if("condition one", "test one (async)", function (done) {
        throw new Error("skipped!");
    });

    skip_if("condition two", "test two", function () {
        console.log("test two!");
    });

});
Gajus
  • 69,002
  • 70
  • 275
  • 438
Louis
  • 146,715
  • 28
  • 274
  • 320
6

I am not sure if this qualifies as „programmatic skipping“, but in order to selectively skip some specific tests for our CI environment, I use Mocha's tagging feature (https://github.com/mochajs/mocha/wiki/Tagging). In describe() or it() messages, you can add a tag like @no-ci. To exclude those tests, you could define a specific "ci target" in your package.json and use --grep and --invert parameters like:

"scripts": {
  "test": "mocha",
  "test-ci" : "mocha --reporter mocha-junit-reporter --grep @no-ci --invert"
}
martin
  • 1,185
  • 17
  • 22
  • This is one of the ways to skip tests. A small example would be really useful. But I definitely agree that the link you shared has an example at the start itself. @martin – Krishna Pravin Feb 25 '20 at 10:42
5

We can write a nice clean wrapper function to conditionally run tests as follows:

function ifConditionIt(title, test) {
  // Define your condition here
  return condition ? it(title, test) : it.skip(title, test);
}

This can then be required and used in your tests as follows:

ifConditionIt('Should be an awesome test', (done) => {
  // Test things
  done();
});
dcr24
  • 374
  • 1
  • 5
  • 11
  • I think this is by far the most elegant solution presented here. It can be extended easily to do more complicated logic, and has the added bonus that tests skipped in this way are marked as skipped in the test report – Joshua Evans Jan 20 '20 at 05:16
2

You can use my package mocha-assume to skip tests programmatically, but only from outside the tests. You use it like this:

assuming(myAssumption).it("does someting nice", () => {});

Mocha-assume will only run your test when myAssumption is true, otherwise it will skip it (using it.skip) with a nice message.

Here's a more detailed example:

describe("My Unit", () => {
    /* ...Tests that verify someAssuption is always true... */

    describe("when [someAssumption] holds...", () => {
        let someAssumption;

        beforeAll(() => {
            someAssumption = /* ...calculate assumption... */
        });

        assuming(someAssumption).it("Does something cool", () => {
            /* ...test something cool... */
        });
    });
});

Using it this way, you can avoid cascading failures. Say the test "Does something cool" would always fail when someAssumption does not hold - But this assumption was already tested above (in Tests that verify someAssuption is always true").

So the test failure does not give you any new information. In fact, it is even a false-positive: The test did not fail because "something cool" did not work, but because a precondition for the test was not satisfied. with mocha-assume you can often avoid such false positives.

Louis
  • 146,715
  • 28
  • 274
  • 320
David Tanzer
  • 2,732
  • 18
  • 30
  • This is really cool, sad that the project seems to be abandoned... – Victor Schröder Jan 13 '20 at 16:33
  • @VictorSchröder Well, I had the impression nobody was using it. Might look into improving it in the next few weeks, if I had the time. Can you open an issue on github and tell me what you'd like to see? – David Tanzer Jan 14 '20 at 04:55
  • I'm not using it yet, @David Tanzer, I just found your idea *really cool*. I see myself doing test preparation and conditional skipping quite a lot and this kind of interface is much more readable. I still have to give it a try, but I imagine that it would be cool to be able to chain several assumptions and support to async functions as assumptions. Maybe all this is already supported, I haven't checked. – Victor Schröder Jan 14 '20 at 10:28
  • 1
    There's a problem, though, with the second example in this answer. The `beforeAll` hook is not guaranteed to run before all the tests are collected. Actually, it's very likely to only run afterwards, but in this case the `assuming(someAssumption)` would have already received the initial (undefined) value. It's needed to wrap that part in a function as well to achieve the desired effect. – Victor Schröder Jan 14 '20 at 15:22
2

This is not really using mocha's features, rather tweaking it to get the behaviour I wanted.

I wanted to skip any subsequent 'it's' in my protractor mocha tests and one 'it' failed. This was because once one step of a journey test failed it was almost certain the rest would fail, and may take a long time and hog the build server if they are using browser waits for elements to appear on a page etc.

When just running standard mocha tests (not protractor) this can be achieved with global beforeEach and afterEach hooks by attaching a 'skipSubsequent' flag to the test's parent (describe) like this:

    beforeEach(function() {
      if(this.currentTest.parent.skipSubsequent) {
            this.skip();
      }
    }); 


    afterEach(function() {
      if (this.currentTest.state === 'failed') {
        this.currentTest.parent.skipSubsequent = 'true'
      }
    })

When attempting this with protractor and mocha it the scope of 'this' has changed and the code above does not work. You end up with an error message like 'error calling done()' and protractor halts.

Instead I ended up with the code below. Not the prettiest, but it ends up replacing the implementation of remaining test functions with a this.skip(). This will probably stop working if/when the internals of mocha change with later versions.

It was figured out through some trial and error by debugging and inspecting mocha's internals...helps make browser test suites complete sooner when the tests fail though.

beforeEach(function() {

    var parentSpec = this.currentTest.parent;

    if (!parentSpec.testcount) {
        parentSpec.testCount = parentSpec.tests.length;
        parentSpec.currentTestIndex = 0;
    } else {
        parentSpec.currentTestIndex = parentSpec.currentTestIndex + 1;
    }

    if (parentSpec.skipSubsequent) {

        parentSpec.skipSubsequent = false;
        var length = parentSpec.tests.length;
        var currentIndex = parentSpec.currentTestIndex;

        for (var i = currentIndex + 1; i < length; i++) {
            parentSpec.tests[i].fn = function() {
                this.skip();
            };
        }
    }
});


afterEach(function() {
    if (this.currentTest.state === 'failed') {
        this.currentTest.parent.skipSubsequent = 'true'
    }
});
1

can be used based on condition, for instance, declare a var and when the condition fails, use this.skip();

Note that skip() does not work in arrow function

let shouldRun: boolean;

before(function(){
if ($('#nsErrorIframe').isDisplayed()) {
        driver.switchToFrame($('#nsErrorIframe'));
        if ($('.ns-error-wrapper').isDisplayed()) {
            console.log('PAGE IS NOT AVAILABLE');
            shouldRun = false;
            if ( shouldRun === false) {
                 this.skip();
                }
                }
  }
});
sindhu
  • 11
  • 2
1

We have some fluky tests in our test environment and sometimes to switch off ones, using this approach:

mocha --config ./config/parallelrc.cjs --parallel --jobs 3 -- tests/spec/**/index.js -g @flaky -i

We mark the flaky tests @flaky in tests describe and set special -g rule, it means that mocha runs only tests with @flaky tag, and the next use -i - it means inversion, so mocha run only tests NOT @flaky.

So, thinking it'll be useful for you)

0

Say I wanted to skip my parametrized test if my test description contained the string "foo", I would do this:

// Skip parametrized test if description contains the string "foo"
(test.description.indexOf("foo") === -1 ? it : it.skip)("should test something", function (done) {
    // Code here
});

// Parametrized tests
describe("testFoo", function () {
        test({
            description: "foo" // This will skip
        });
        test({
            description: "bar" // This will be tested
        });
});

In your case, I believe that if you wanted to check environment variables, you could use NodeJS's:

process.env.ENV_VARIABLE

For example (Warning: I haven't tested this bit of code!), maybe something like this:

(process.env.NODE_ENV.indexOf("prod") === -1 ? it : it.skip)("should...", function(done) {
    // Code here
});

Where you can set ENV_VARIABLE to be whatever you are keying off of, and using that value, skip or run the test. (FYI the documentation for the NodeJS' process.env is here: https://nodejs.org/api/process.html#process_process_env)

I won't take complete credit for the first part of this solution, I found and tested the answer and it worked perfectly to skip tests based on a simple condition through this resource: https://github.com/mochajs/mocha/issues/591

Hope this helps! :)

Rubicon
  • 137
  • 7
0
mocha test/ --grep <pattern>

https://mochajs.org/

James Akwuh
  • 2,169
  • 1
  • 23
  • 25
0

I wanted this feature so that I could sometimes skip slow tests within the test suite. I put these at the top of my test file and use them as needed...

const describeMaybe = Boolean(process.env.SKIP_SLOW) ? describe.skip : describe
const itMaybe = Boolean(process.env.SKIP_SLOW) ? it.skip : it

describe('my test', () => {
  it(...) // this test always runs
  itMaybe(...) // this test gets skip based on the env var
})
ryanrhall
  • 33
  • 2
-1

As @danielstjules answered here there is a way to skip test. @author of this topic has copied answer from github.com mochajs discussion, but there is no info in what version of mocha it available.

I'm using grunt-mocha-test module for integrating mocha test functionality in my project. Jumping to last (for now) version - 0.12.7 bring me mocha version 2.4.5 with implementation of this.skip().

So, in my package.json

  "devDependencies": {
    "grunt-mocha-test": "^0.12.7",
    ...

And then

npm install

And it make me happy with this hook:

describe('Feature', function() {

    before(function () {

        if (!Config.isFeaturePresent) {

            console.log('Feature not configured for that env, skipping...');
            this.skip();
        }
    });
...

    it('should return correct response on AB', function (done) {

        if (!Config.isABPresent) {

           return this.skip();
        }

        ...
Victor Perov
  • 1,697
  • 18
  • 37
-5

Please don't. A test that doesn't work consistently across environments should be acknowledged as such by your build infrastructure. And it can be very disorienting when the CI builds have a different number of tests run than local.

Also it screws up repeatability. If different tests run on the server and local I can have tests failing in dev and passing in CI or vice versa. There's no forcing function and I have no way to quickly and accurately correct a failed build.

If you must turn off tests between environments, instead of conditionally running tests, tag your tests and use a filter to eliminate tests that don't work in certain build targets. That way everybody knows what's going on and it tempers their expectations. It also lets everybody know that there's inconsistency in the test framework, and someone might have a solution that gets them running properly again. If you just mute the test they might not even know there's a problem.

Jason
  • 3,021
  • 1
  • 23
  • 25
  • 1
    Taking into consideration that a functional test acts inconsistently between environments (local and dev) is fine. Your CI should fail accordingly before your Pull Request completes. Code is the correct place to document these inconsistencies. Configuration file work-arounds are more likely to hide things. This is why it.skip is in code and not a configuration file. – TamusJRoyce Sep 11 '20 at 16:15