expect() 没有实际期望
expect() with no actual expectations
问题:
最近,在审查我们现有的测试代码库时,我注意到在没有 "matching" 部分的情况下使用 expect()
时会出现一种危险的 typo/mistake:
expect(page.filters.fromDateLabel.getText(), "After");
我很确定 toEqual()
是要在这里使用的:
expect(page.filters.fromDateLabel.getText()).toEqual("After");
问题是 jasmine
在这种情况下不会辜负预期(好吧,显然是因为实际上没有预期)。这让我们遇到了一个更严重的问题——在测试用例中实际上没有测试任何东西——它在没有期望的情况下通过了。我们对测试的内容产生了错误的认识。
问题:
我想尽快发现这些错误。你认为我应该如何处理这个问题?
想法:
- 如果没有预期,测试用例会以某种方式失败(不确定
jasmine
是否内置了类似的东西)
- "patch"
expect()
并在 "expect" 部分没有调用时发出 warning/raise 错误
- 使用静态代码分析 - 定义自定义
eslint
规则
我倾向于认为静态分析路线是最好的,但如果您正在寻找一种快速而肮脏的方法,这里有一些代码可以获取所有对 expect
的调用返回的期望并创建一个跟踪是否使用过任何期望属性的代理:
var unusedExpectations = new Set();
var originalExpect = window.expect; // Should be empty after every spec
var expect = function() {
var rawExpectation = originalExpect.apply(this, arguments);
unusedExpectations.add(rawExpectation); // Assume unused until used
// Traverse expectation and its prototypes, copying all properties to
// our proxy object. (Note that this becomes much simpler if you have
// ES6 Proxy in your environment.)
var proxy = {}
for(var proto = rawExpectation; proto; proto = proto.__proto__) {
Object.getOwnPropertyNames(proto).forEach(function(prop) {
if(Object.getOwnPropertyDescriptor(proxy, prop))
return;
Object.defineProperty(
proxy, prop, {
get: function() {
// Aha! Somebody used this expectation for _something_.
unusedExpectations.delete(rawExpectation);
return rawExpectation[prop];
}
}
);
});
}
return proxy;
}
把它放在你的规格中隐藏 Jasmine 的 expect
的地方,然后:
beforeEach(function() {
unusedExpectations.clear();
});
afterEach(function() {
expect(unusedExpectations.size).toEqual(0);
});
注意事项:
- 有点邪恶。
- 不会捕捉到
expect(foo).toBeFalsy;
(缺少括号)。
- 计算any属性的使用,所以不会catch
expect(foo).toString()
.
仍然有效!
可以添加代码来检查堆栈跟踪并提取有问题的位置 expect()
,但我认为标记哪个规范有未使用的 expect()
就足够了。
答案中提供的自定义 ESLint 规则现在是 eslint-plugin-jasmine
1.6.0:
的一部分
旧答案:
这是 custom ESLint rule 我最后得到的:
module.exports = function (context) {
return {
// checking "expect()" arguments
CallExpression: function (node) {
if (node.callee.name === 'expect') {
if (node.arguments.length > 1) {
context.report(node, 'More than one argument passed to expect()')
} else if (node.arguments.length === 0) {
context.report(node, 'No arguments passed to expect()')
}
}
},
// nothing called on "expect()"
'CallExpression:exit': function (node) {
if (node.callee.name === 'expect' && node.parent.type === 'ExpressionStatement') {
context.report(node, 'Nothing called on expect()')
}
}
}
}
它检查 3 件事:
- 超过 1 个参数传递给
expect()
- 没有参数传递给
expect()
expect()
上没有任何调用
这是它当前捕获的无效 expect()
用法示例:
expect(page.filters.fromDateLabel.getText(), "After");
expect("After");
expect();
至于选项 #1,实际上有一个非常相关且有用的 ESLint
规则已经由 [eslint-plugin-jasmine
] 实现并开源:
问题:
最近,在审查我们现有的测试代码库时,我注意到在没有 "matching" 部分的情况下使用 expect()
时会出现一种危险的 typo/mistake:
expect(page.filters.fromDateLabel.getText(), "After");
我很确定 toEqual()
是要在这里使用的:
expect(page.filters.fromDateLabel.getText()).toEqual("After");
问题是 jasmine
在这种情况下不会辜负预期(好吧,显然是因为实际上没有预期)。这让我们遇到了一个更严重的问题——在测试用例中实际上没有测试任何东西——它在没有期望的情况下通过了。我们对测试的内容产生了错误的认识。
问题:
我想尽快发现这些错误。你认为我应该如何处理这个问题?
想法:
- 如果没有预期,测试用例会以某种方式失败(不确定
jasmine
是否内置了类似的东西) - "patch"
expect()
并在 "expect" 部分没有调用时发出 warning/raise 错误 - 使用静态代码分析 - 定义自定义
eslint
规则
我倾向于认为静态分析路线是最好的,但如果您正在寻找一种快速而肮脏的方法,这里有一些代码可以获取所有对 expect
的调用返回的期望并创建一个跟踪是否使用过任何期望属性的代理:
var unusedExpectations = new Set();
var originalExpect = window.expect; // Should be empty after every spec
var expect = function() {
var rawExpectation = originalExpect.apply(this, arguments);
unusedExpectations.add(rawExpectation); // Assume unused until used
// Traverse expectation and its prototypes, copying all properties to
// our proxy object. (Note that this becomes much simpler if you have
// ES6 Proxy in your environment.)
var proxy = {}
for(var proto = rawExpectation; proto; proto = proto.__proto__) {
Object.getOwnPropertyNames(proto).forEach(function(prop) {
if(Object.getOwnPropertyDescriptor(proxy, prop))
return;
Object.defineProperty(
proxy, prop, {
get: function() {
// Aha! Somebody used this expectation for _something_.
unusedExpectations.delete(rawExpectation);
return rawExpectation[prop];
}
}
);
});
}
return proxy;
}
把它放在你的规格中隐藏 Jasmine 的 expect
的地方,然后:
beforeEach(function() {
unusedExpectations.clear();
});
afterEach(function() {
expect(unusedExpectations.size).toEqual(0);
});
注意事项:
- 有点邪恶。
- 不会捕捉到
expect(foo).toBeFalsy;
(缺少括号)。 - 计算any属性的使用,所以不会catch
expect(foo).toString()
.
仍然有效!
可以添加代码来检查堆栈跟踪并提取有问题的位置 expect()
,但我认为标记哪个规范有未使用的 expect()
就足够了。
答案中提供的自定义 ESLint 规则现在是 eslint-plugin-jasmine
1.6.0:
旧答案:
这是 custom ESLint rule 我最后得到的:
module.exports = function (context) {
return {
// checking "expect()" arguments
CallExpression: function (node) {
if (node.callee.name === 'expect') {
if (node.arguments.length > 1) {
context.report(node, 'More than one argument passed to expect()')
} else if (node.arguments.length === 0) {
context.report(node, 'No arguments passed to expect()')
}
}
},
// nothing called on "expect()"
'CallExpression:exit': function (node) {
if (node.callee.name === 'expect' && node.parent.type === 'ExpressionStatement') {
context.report(node, 'Nothing called on expect()')
}
}
}
}
它检查 3 件事:
- 超过 1 个参数传递给
expect()
- 没有参数传递给
expect()
expect()
上没有任何调用
这是它当前捕获的无效 expect()
用法示例:
expect(page.filters.fromDateLabel.getText(), "After");
expect("After");
expect();
至于选项 #1,实际上有一个非常相关且有用的 ESLint
规则已经由 [eslint-plugin-jasmine
] 实现并开源: