Повествование: научиться писать модульные тесты было для меня чем-то вроде головокружения. Поначалу это было очень неприятно и неудовлетворительно, но со временем стало довольно весело. Изучение того, как читать запросы на слияние для новых файлов модульных тестов или правок в существующих, усугубило путаницу и, следовательно, потребовало вернуться к основам рассмотрения того, что тесты должны делать снова. Это моя рубрика-напоминание для чтения этих 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 );
- Являются ли человеческие описания в
describe
s и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
блок.