Ожидать() без реальных ожиданий
Проблема:
В последнее время, просматривая нашу существующую тестовую кодовую базу, я заметил опасный тип опечатки/ошибки, когда expect()
использовался без "соответствующей" части:
expect(page.filters.fromDateLabel.getText(), "After");
Я уверен, что toEqual()
должен был использоваться здесь:
expect(page.filters.fromDateLabel.getText()).toEqual("After");
Проблема заключается в том, что jasmine
не подведет ожидания в этом случае (ну, очевидно, потому что ничего не ожидалось). И это ставит нас к более серьезной проблеме - ничто не было фактически проверено в тестовом примере - это проходило без каких-либо ожиданий. Мы получили ложное представление о том, что было проверено.
Вопрос:
Я хочу поймать эти ошибки как можно быстрее. Как вы думаете, как я должен справиться с этой проблемой?
Мысли:
- каким-то образом отказался от тестового примера, если в нем не было ожиданий (не уверен, что
jasmine
имеет что-то подобное этому встроенному)
- "запланировать"
expect()
и выпустить предупреждение/повысить ошибку, если ничего не вызывалось в части "ожидать"
- использовать статический анализ кода - определить пользовательское правило
eslint
Ответы
Ответ 1
Пользовательское правило ESLint, предоставленное в ответе, теперь является частью eslint-plugin-jasmine
1.6.0:
Старый ответ:
Вот правило ESLint. Я закончил с:
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
]:
Ответ 2
Я склонен думать, что маршрут статического анализа лучше всего, но если вы ищете быстрый и грязный способ, то есть какой-то код, который захватывает ожидания, возвращенные всеми вызовами 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;
}
Поместите это в место, где он скрывает Jasmines expect
от ваших спецификаций, а затем:
beforeEach(function() {
unusedExpectations.clear();
});
afterEach(function() {
expect(unusedExpectations.size).toEqual(0);
});
Предостережения:
- Вид зла.
- Не поймает
expect(foo).toBeFalsy;
(отсутствуют парсеры).
- Подсчитывает использование любого свойства, поэтому не поймайте
expect(foo).toString()
.
Тем не менее, он работает!
Можно было бы добавить код, чтобы проверить трассировку стека и извлечь местоположение оскорбительного expect()
, но я думаю, что помечать, какая спецификация имеет неиспользованный expect()
, достаточно.