Повествование: научиться писать модульные тесты было для меня чем-то вроде головокружения. Поначалу это было очень неприятно и неудовлетворительно, но со временем стало довольно весело. Изучение того, как читать запросы на слияние для новых файлов модульных тестов или правок в существующих, усугубило путаницу и, следовательно, потребовало вернуться к основам рассмотрения того, что тесты должны делать снова. Это моя рубрика-напоминание для чтения этих PR. Де-факто он служит руководством по написанию тестов, которым я могу доверять, поддерживать и с уверенностью передавать другим.

Использование: указанный ниже порядок является моим собственным приоритетом, и не все из них являются обязательными. Шаги с более низким приоритетом — это те, которые можно, так сказать, ~разблокировать~, поскольку шаги с более высоким приоритетом становятся более легкими для писателя/рецензента. Например, я использую псевдокод, основанный на нескольких фреймворках, но, надеюсь, принципы могут быть расширены за его пределы.

  • Оказывают ли describe и it достаточное давление на модуль? Если модуль должен иметь какое-то поведение и проверяться на несколько случаев ошибок, соответствуют ли тесты этому минимальному минимуму? Можете ли вы, как человек, придумать несколько сценариев или случаев ошибок, которые существуют в реальности? Представлены ли они в тестах и/или коде?
  • Можете ли вы изменить что-то в тестовом файле — например, бессмысленный аргумент — и он будет вести себя (сломаться) ожидаемым образом?
isGonnaBeOkay.calledWithMatch( "a-okay" ) // passes
/* enacting your pernicious testing interrogation: */
isGonnaBeOkay.calledWithMatch( "def-not-okay" ) // should fail
  • Являются ли утверждения прозрачными в отношении того, что они утверждают? например.:
/* sufficient */
someFunc.getCall( 0 ).arg[ 1 ].should.equal( false )
/* ideal */
const isEnabledForUser = someFunc.getCall( 0 ).arg[ 1 ];
isEnabledForUser.should.equal( false );
  • Являются ли человеческие описания в describes и it точным поведением модуля?
/* more like an integration test */
describe( "Some desired behavior that requires context", () => {} );
/* ideal and explicit unit test */
describe( "Missing parameter and resulting code path", () => {} );
  • Теперь, когда человеческие описания соответствуют модулю, действительно ли утверждения следуют порядку, требуемому человеческими описаниями? describe( "This argument change means a function later on will be called with this different argument", () => {} ); должен проверять не только то, что функция была вызвана, например, но и то, что она была вызвана с этим и только с этим аргументом.
  • Можно ли удалить какие-либо настройки? Сделано что-то сверхмощное заглушка или шпион, что можно упростить? может неоперативка?
/* lots of code, hard to tell what's meant to happen */
const someFunction = stub().returns( "hello" );
const someOtherFunction = stub();
const someThirdFunction = spy();
const helloFactory = stub().returns( () => someFunction() );
describe( "helloFactory was called once, does stuff", () => {} );

/* simple, easy to modify as-needed if module changes/grows */
const someFunction = () => "hello";
const helloFactory = stub().returns( () => someFunction() );
describe( "helloFactory was called once, does stuff", () => {} );
  • Являются ли ожидаемые результаты переменными и далекими от утверждения или жестко закодированы и/или близки?
/* in large modules, can be hard to see what's actually breaking */
enumFunc.should.return( someExampleObjectToScrollTo );
/* easier to see where/why something might have broken */
enumFunc.should.return( {
  firstValue: "one",
  secondValue: "two",
  thirdValue: "three" // test fails by returning "gamma"
} );

Как учитывать покрытие.Автоматизированное покрытие кода не обязательно является покрытием кода. Он используется в качестве ограждения для проверки того, что каждая строка была затронута путем кода. Если есть несколько утверждений для важных шагов на этом пути, зачем вообще писать тестовые файлы? Отчеты о покрытии могут быть очень полезны для рецензента, если какой-то путь кода не был затронут, как утверждает человеческий describe блок.