шаблон
Интересный вопрос. Прежде всего - мой окончательный тестовый шаблон, настроенный в IDE:
@Test
public void shouldDoSomethingWhenSomeEventOccurs() throws Exception
{
//given
//when
//then
}
Я всегда начинаю с этого кода (умные люди называют его BDD ).
В given
Я размещаю настройку теста, уникальную для каждого теста.
when
в идеале - одна строка - то, что вы тестируете.
then
должен содержать утверждения.
Я не единственное утверждение защитник, однако вы должны проверить только один аспект поведения. Например, если метод должен что-то возвращать, а также имеет некоторые побочные эффекты, создайте два теста с одинаковыми разделами given
и when
.
Также тестовая таблица включает в себя throws Exception
. Это для обработки надоедливых проверенных исключений в Java. Если вы протестируете какой-то код, который их генерирует, компилятор вас не побеспокоит. Конечно, если тест выдает исключение, он терпит неудачу.
Настройка
Тестовая настройка очень важна. С одной стороны, разумно извлечь общий код и поместить его в метод setup()
/ @Before
. Тем не менее, обратите внимание, что при чтении теста ( и удобочитаемость является наибольшим значением в модульном тестировании! ), легко пропустить установочный код, висящий где-то в начале тестового примера. Таким образом, соответствующая настройка теста (например, вы можете создать виджет различными способами) должна идти к методу теста, но инфраструктура (настройка общих макетов, запуск встроенной тестовой базы данных и т. Д.) Должна быть извлечена. Еще раз для улучшения читаемости.
Также известно ли вам, что JUnit создает новый экземпляр класса тестового набора для каждого теста? Таким образом, даже если вы создадите свой CUT ( тестируемый класс ) в конструкторе, конструктор вызывается перед каждым тестом. Вид раздражает.
* * Зернистость тысяча сорок-девять
Назовите ваш тест и подумайте, какой вариант использования или функциональность вы хотите протестировать, никогда не думайте с точки зрения:
это класс Foo
с методами bar()
и buzz()
, поэтому я создаю FooTest
с testBar()
и testBuzz()
. О, дорогой, мне нужно протестировать два пути выполнения на всем протяжении bar()
- поэтому давайте создадим testBar1()
и testBar2()
.
shouldTurnOffEngineWhenOutOfFuel()
- это хорошо, testEngine17()
- это плохо.
Подробнее о наименовании
Что имя testGetBuzzWhenFooIsNullAndFizzIsNonNegative
говорит о тесте? Я знаю это тесты что-то, но почему? И вы не думаете, что детали слишком интимны? Как насчет:
@Test shouldReturnDisabledBuzzWhenFooNotProvidedAndFizzNotNegative`
Он описывает смысл ввода и ваше намерение (при условии, что отключенное жужжание является своего рода buzz
статус / тип). Также обратите внимание, что у нас больше нет жесткого кода getBuzz()
имени метода и null
контракта для Foo
(вместо этого мы говорим: , когда Foo
не предоставлено ). Что если в будущем вы замените null
на шаблон нулевого объекта ?
Также не бойтесь 20 различных методов испытаний для getBuzz()
. Вместо этого подумайте о 20 различных вариантах использования, которые вы тестируете. Однако, если ваш класс тестового примера становится слишком большим (поскольку он обычно намного больше, чем тестируемый класс), разбейте его на несколько тестовых случаев. Еще раз: FooHappyPathTest
, FooBogusInput
и FooCornerCases
- это хорошо, Foo1Test
и Foo2Test
- это плохо.
читаемость
Стремитесь к коротким и описательным именам. Несколько строк в given
и несколько строк в then
. Вот и все. Создавайте компоновщики и внутренние DSL, извлекайте методы, пишите пользовательские сопоставления и утверждения. Тест должен быть даже более читабельным, чем производственный код. Не издевайся.
Я считаю полезным сначала написать серию пустых, хорошо названных методов тестового примера. Затем я возвращаюсь к первому. Если я все еще понимаю, что я должен был тестировать при каких условиях, я тем временем реализую тест, строящий API класса. Затем я реализую этот API. Умные люди называют это TDD (см. Ниже).
Рекомендуемое чтение: