我有一个代码,其中某些测试在CI环境中总是失败。我想根据环境条件禁用它们。

如何在运行时执行期间以编程方式跳过mocha测试?


当前回答

请不要。在不同环境中不能一致工作的测试应该得到构建基础设施的承认。当CI构建运行的测试数量与本地构建不同时,可能会非常令人困惑。

它还破坏了重复性。如果不同的测试运行在服务器和本地,我可以有测试失败在开发和通过CI,反之亦然。没有强制功能,我无法快速准确地纠正失败的构建。

如果必须关闭环境之间的测试,而不是有条件地运行测试,请标记您的测试并使用过滤器来消除在某些构建目标中不起作用的测试。这样每个人都知道发生了什么,并降低他们的期望。它还让每个人都知道测试框架中存在不一致,并且有人可能有一个解决方案,可以使它们再次正常运行。如果你只是静音测试,他们可能甚至不知道有问题。

其他回答

我们可以编写一个整洁的包装器函数来有条件地运行测试,如下所示:

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

然后可以在您的测试中要求并按以下方式使用:

ifConditionIt('Should be an awesome test', (done) => {
  // Test things
  done();
});

我不确定这是否可以称为“程序性跳过”,但是为了有选择性地跳过我们CI环境中的某些特定测试,我使用了Mocha的标记功能(https://github.com/mochajs/mocha/wiki/Tagging)。 在describe()或it()消息中,可以添加@no-ci这样的标记。要排除这些测试,可以在包中定义特定的“ci目标”。使用——grep和——invert参数,如下:

"scripts": {
  "test": "mocha",
  "test-ci" : "mocha --reporter mocha-junit-reporter --grep @no-ci --invert"
}

这并不是真正使用摩卡的功能,而是调整它以获得我想要的行为。

我想在量角器摩卡测试中跳过所有后续的“it’s”,但有一个“it’s”失败了。这是因为一旦旅程测试的一个步骤失败,几乎可以肯定其他步骤也会失败,并且可能需要很长时间,如果他们使用浏览器等待元素出现在页面上等,可能会占用构建服务器。

当只是运行标准摩卡测试(不是量角器)时,这可以通过全局beforeEach和afterEach钩子来实现,在测试的父(描述)上附加一个' skipfollow '标志,如下所示:

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


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

当尝试使用量角器和摩卡时,“this”的范围发生了变化,上面的代码不起作用。你最终会得到一个错误消息,比如'error calling done()',量角器会停止。

相反,我最终得到了下面的代码。不是最漂亮的,但它最终用this.skip()替换了其余测试函数的实现。如果/当摩卡的内部结构在后续版本中发生变化时,这可能会停止工作。

这是通过调试和检查摩卡内部的一些试验和错误发现的……不过,当测试失败时,有助于更快地完成浏览器测试套件。

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'
    }
});

您可以通过在describe或it块前面放置x或在其后放置.skip来跳过测试。

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

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

您还可以通过在测试上放置.only来运行单个测试。例如

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

在这种情况下,只有特征2块会运行。

似乎没有一种通过编程方式跳过测试的方法,但是您可以在beforeEach语句中进行某种检查,并且只在设置了标志的情况下运行测试。

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,
         }
    }
}

这取决于您希望如何以编程方式跳过测试。如果在运行任何测试代码之前可以确定跳过的条件,那么您可以直接调用它或它。根据需要,根据条件跳过。例如,如果环境变量ONE被设置为任何值,这将跳过一些测试:

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!");
    });

});

如果您想要检查的条件只能在测试时确定,那就有点复杂了。如果你不想访问任何严格来说不是测试API的部分,那么你可以这样做:

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!");
    });

});

虽然我的第一个例子是将测试标记为正式跳过(又名“pending”),但我刚才展示的方法只是避免执行实际的测试,但测试不会标记为正式跳过。他们将被标记为通过。如果你真的想跳过它们,我不知道有什么方法可以访问不是测试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!");
    });

});