Ожидать() без реальных ожиданий

Проблема:

В последнее время, просматривая нашу существующую тестовую кодовую базу, я заметил опасный тип опечатки/ошибки, когда 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(), достаточно.