是否可以选择将mocha设置为报告测试为失败,以防万一(?)函数中没有提供预期?
理念是工作流程如下:
所以主要意图是在进行规范TDD风格开发新增测试时报告为失败,直到设置期望(或测试设置为挂起而没有回调或skip()),再次报告为失败并且一旦执行完成它据报道是成功的。
我看到它的价值()成功而没有期望的是,一旦它被添加,它的失败现在证明它实际上正在工作并证明它失败了。是故意还是我遗失了什么?
另外,如果有人知道如何在karma.conf中设置它会很好。
由于
答案 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` ) );
}
} );