如何让mocha在没有预期的情况下失败()

时间:2015-06-22 02:32:02

标签: unit-testing tdd mocha karma-mocha mocha-phantomjs

是否可以选择将mocha设置为报告测试为失败,以防万一(?)函数中没有提供预期?

理念是工作流程如下:

  1. 使用desc和回调函数添加一个it()
  2. 由于回调中未设置任何预期,因此it()报告为失败
  3. 期望已添加
  4. it()仍然报告为失败,因为没有实现,因为没有实现
  5. 实施已添加
  6. it()报告为成功
  7. 重构
  8. 所以主要意图是在进行规范TDD风格开发新增测试时报告为失败,直到设置期望(或测试设置为挂起而没有回调或skip()),再次报告为失败并且一旦执行完成它据报道是成功的。

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

    另外,如果有人知道如何在karma.conf中设置它会很好。

    由于

3 个答案:

答案 0 :(得分:1)

通过设置标志,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进行检查,我们将done替换为我们自己的标记,以便在调用它时进行标记。

关于异步测试和共享状态的说明。有时人们认为如果将Mocha标记为异步,它们将同时运行多个。通常情况并非如此。在异步测试后继续执行之前,Mocha会等待两件事之一:测试调用其expected_called回调或超时。如果先前的测试超时可以同时运行两个测试的代码,那么超时的测试实际上正在等待异步操作超时后完成。在这种情况下,如果存在两个测试都依赖的任何状态,则超时可能导致级联测试失败(或级联测试成功!)。这是Mocha的一个普遍问题。一旦超时问题得到解决,那么级联效应将消失,后续测试将根据自己的优点成功或失败,而不会受到超时的早期异步测试的影响。在上面的代码中,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 () {}); 私有实例,它只会增加自己的私有计数器。这可以按如下方式完成:

expect

但缺点是你现在必须以this.expect的身份访问expect,这意味着编写测试的方式与通常不同。您可能认为在每次测试之前设置全局this将消除使用expect的需要,但这种方法将完全 与上面讨论的问题相同。 (测试共享的全局状态将是expect_called本身而不是 plt_d = d[s & grepl("Rd", d$Cmd), c("Time_Stamp_Norm", "Virtual_Address_Norm")] p1 <- qplot(Time_Stamp_Norm, Virtual_Address_Norm, data=plt_d, geom='bin2d', binwidth = hist_binwidth, xlab="Normalized Time", ylab="Normalized Address", main="Read requests in virtual address space") + scale_fill_gradient(low="#C6DBEF", high="#08306B") + xlim(0, 1+b_inv) + ylim(0, 1+b_inv) + theme(axis.text=element_text(size=10), axis.title=element_text(size=10), plot.title=element_text(size=10)) plt_d = d[s & grepl("Wr", d$Cmd), c("Time_Stamp_Norm", "Virtual_Address_Norm")] p2 <- qplot(Time_Stamp_Norm, Virtual_Address_Norm, data=plt_d, geom='bin2d', binwidth = hist_binwidth, xlab="Normalized Time", ylab="Normalized Address", main="Write requests in virtual address space") + scale_fill_gradient(low="#C7E9C0", high="#00441B") + xlim(0, 1+b_inv) + ylim(0, 1+b_inv) + theme(axis.text=element_text(size=10), axis.title=element_text(size=10), plot.title=element_text(size=10)) ... print(p1, vp = viewport(layout.pos.row = 1, layout.pos.col = 1)) print(p2, vp = viewport(layout.pos.row = 1, layout.pos.col = 2)) ... dev.off() 。)

答案 1 :(得分:0)

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

答案 2 :(得分: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` ) );
    }

} );