8

如果 it() 函数中没有提供期望,是否可以设置 mocha 以将测试报告为失败?

想法是这样的工作流程:

  1. 添加一个带有 desc 和回调函数的 it()
  2. it() 被报告为失败,因为回调中没有设置期望
  3. 增加了期望
  4. it() 仍然报告为失败,因为没有实现,因为没有达到预期
  5. 添加了实现
  6. it() 被报告为成功
  7. 重构

因此,主要意图是在进行规范 TDD 样式开发时,新添加的测试报告为失败,直到设置期望(或测试集为未决而没有回调或跳过()),这再次报告为失败,一旦实施完成,报告为成功。

我看到 it() 成功而不期望的价值是,一旦添加它,它的失败现在证明它实际上正在工作并证明它正在失败。是故意的还是我错过了什么?

此外,如果有人知道如何在 karma.conf.js 中进行设置,那就太好了。

谢谢

4

4 回答 4

2

Mocha 不支持您仅通过设置标志来执行的操作。最接近的是it不带回调使用:

`it("foo")`

Mocha 将按此方式处理此测试pending并进行报告。这与使用it.skip(...). 然而,测试并没有失败,它也没有捕捉到愚蠢的错误,比如有一个实际上没有迭代的循环:

it("foo", function () {
    var a = something();
    for (var i = 0; i < a.length; ++i) {
        expect(a[i]).to...
    }
});

如果碰巧这a是一个长度为 0 的数组,那么您将不会测试任何东西,并且测试将通过。在这种情况下,我测试数组不是 0 长度,但仍然......

所以没有直接的方法可以做到这一点,而且 Mocha 没有为断言库提供 API 来告诉 Mocha 它们实际上已在测试中使用。不过,您可以构建自己的解决方案。这是一个概念证明:

var real_expect = require("chai").expect;

var expect_called = 0;

function expect() {
    expect_called++;
    return real_expect.apply(this, arguments);
}

var real_it = it;

it = function (name, fn) {
    if (!fn.length) {
        // Handle the case where `fn` is declared to be synchronous.
        real_it(name, function () {
            expect_called = 0;
            fn.call(this);
            if (expect_called === 0)
                throw new Error("test did not call expect");
        });
    }
    else {
        // Handle the case where `fn` is declared to be asynchronous.
        real_it(name, function (real_done) {
            expect_called = 0;
            function done () {
                if (expect_called === 0) {
                    done(new Error("test did not call expect"));
                    return;
                }
                real_done();
            }
            fn.call(this, done);
        });
    }
};

it("foo", function () {
    expect(1).to.equal(1);
});

it("foo async", function (done) {
    setTimeout(function () {
        expect(1).to.equal(1);
        done();
    }, 1000);
});

it("bar", function () {});
it("bar 2", function () {});

在上面的代码中,我们替换it为我们自己的,它会进行检查,并expect在调用它时替换为我们自己的标记。

关于异步测试和共享状态的说明。有时人们认为,如果将 Mocha 标记为异步,它们会同时运行多个。通常情况并非如此。Mocha 在异步测试之后继续等待两件事之一:测试调用其done回调或超时。如果较早的测试超时,您可以同时运行来自两个测试的代码,并且碰巧超时的测试实际上是在等待异步操作在之后完成超时。在这种情况下,如果两个测试都依赖于任何状态,超时可能会导致级联测试失败(或级联测试成功!)。这是 Mocha 的普遍问题。一旦超时问题得到解决,那么级联效应将消失,后续测试将根据自身的优点成功或失败,而不会受到早期超时的异步测试的影响。在上面的代码中,expected_called是所有测试所依赖的状态。所以超时可能会导致级联效应。

为了解决这个问题,每个测试都必须有它自己的私有实例expect,这只会增加它自己的私有计数器。这可以按如下方式完成:

var real_expect = require("chai").expect;

var real_it = it;

it = function (name, fn) {
    if (!fn.length) {
        // Handle the case where `fn` is declared to be synchronous.
        real_it(name, function () {
            var expect_called = 0;

            this.expect = function () {
                expect_called++;
                return real_expect.apply(this, arguments);
            };

            fn.call(this);
            if (expect_called === 0)
                throw new Error("test did not call expect");
        });
    }
    else {
        // Handle the case where `fn` is declared to be asynchronous.
        real_it(name, function (real_done) {
            var expect_called = 0;

            this.expect = function () {
                expect_called++;
                return real_expect.apply(this, arguments);
            };

            function done () {
                if (expect_called === 0) {
                    done(new Error("test did not call expect"));
                    return;
                }
                real_done();
            }

            fn.call(this, done);
        });
    }
};

it("foo", function () {
    this.expect(1).to.equal(1);
});

it("foo async", function (done) {
    var me = this;
    setTimeout(function () {
        me.expect(1).to.equal(1);
        done();
    }, 1000);
});

it("bar", function () {});
it("bar 2", function () {});

但缺点是您现在必须访问expectas this.expect,这意味着编写测试的方式与通常不同。您可能认为expect在每次测试之前设置全局将消除使用的需要,this但这种方法会遇到与我上面讨论的完全相同的问题。(测试共享的全局状态将是expect它本身而不是expect_called。)

于 2015-06-22T16:39:48.657 回答
0

我知道这是一个老问题,但如果其他人正在寻找这个,有一个更简单的方法,使用 Sinon。您只需将这个文件放在每个测试文件的顶部即可。

let checkForExpectation = [];

// before each test...
beforeEach( () => {

    // ... spy on the expect functions so we will know whether they are called
    checkForExpectation = [
        sinon.spy( chai.expect, 'fail' ),
        sinon.spy( chai, 'expect' )
        // you can also add spies for "assert" here with a loop, but "should" is much harder
    ];

} );

// after each test ...
afterEach( function() { // must use "function()" due to needing `this`

    // ... look for one of the expect functions to have been called
    const called = !!checkForExpectation.find( spy => spy.called );
    checkForExpectation = undefined;

    // ... restore the sinon contexts to their initial state
    sinon.restore();

    // ... create an error for the test that has just ended
    if ( !called && this.currentTest.state !== 'failed' ) {
        this.test.error( new chai.AssertionError( `Test "${this.currentTest.title}" contained no expect statement` ) );
    }

} );
于 2020-09-18T22:25:28.647 回答
0

Mocha 9.1 添加了在没有遇到测试时使用错误代码(“非零退出代码”)退出的--fail-zero选项。mocha

不确定这是否是您正在寻找的 100%,但这是我遇到此问答时所寻找的。当定义了一些测试但不是在每个it()块上时,这可能不起作用。我没有测试过。

于 2022-02-10T23:42:36.000 回答
-2

这里的解决方案可能在原始答案之后已经存在,但它是将done回调传递给测试用例。如此处所述:https ://blog.cloudboost.io/javascript-asynchronous-testing-gotchas-ac7e5c39257

于 2019-05-10T14:08:59.633 回答