Следует ли инициализировать поля класса при таком объявлении?
public class SomeTest extends TestCase
{
private final List list = new ArrayList();
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Или в setUp () вот так?
public class SomeTest extends TestCase
{
private List list;
@Override
protected void setUp() throws Exception
{
super.setUp();
this.list = new ArrayList();
}
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Я предпочитаю использовать первую форму, потому что она более краткая и позволяет мне использовать поля final. Если мне не нужно использовать метод setUp () для настройки, следует ли мне его использовать и почему?
Уточнение:
JUnit будет создавать экземпляр тестового класса один раз для каждого тестового метода. Это означает, list
что будет создаваться один раз за тест, независимо от того, где я это объявляю. Это также означает, что между тестами нет временных зависимостей. Похоже, что в использовании setUp () нет никаких преимуществ. Однако в JUnit FAQ есть много примеров, которые инициализируют пустую коллекцию в setUp (), поэтому я полагаю, что для этого должна быть причина.
Ответы:
Если вас интересуют конкретные примеры в FAQ JUnit, такие как базовый тестовый шаблон , я думаю, что лучшая практика, демонстрируемая там, заключается в том, что тестируемый класс должен быть создан в вашем методе setUp (или в тестовом методе) ,
Когда примеры JUnit создают ArrayList в методе setUp, все они переходят к тестированию поведения этого ArrayList с такими случаями, как testIndexOutOfBoundException, testEmptyCollection и т.п. Есть перспектива того, что кто-то напишет класс и убедится, что он работает правильно.
Вероятно, вам следует сделать то же самое при тестировании собственных классов: создать свой объект в setUp или в тестовом методе, чтобы вы могли получить разумный результат, если позже вы его сломаете.
С другой стороны, если вы используете класс коллекции Java (или другой класс библиотеки, если на то пошло) в своем тестовом коде, это, вероятно, не потому, что вы хотите его протестировать - это просто часть тестового приспособления. В этом случае вы можете с уверенностью предположить, что он работает по назначению, поэтому инициализировать его в объявлении не составит труда.
Как бы то ни было, я работаю над достаточно большой кодовой базой, разработанной TDD уже несколько лет назад. Обычно мы инициализируем объекты в их объявлениях в тестовом коде, и за те полтора года, что я работаю над этим проектом, это ни разу не вызвало проблем. Так что есть по крайней мере некоторые анекдотические свидетельства того, что это разумный поступок.
источник
Я начал копать сам и нашел одно потенциальное преимущество использования
setUp()
. Если во время выполнения возникнут какие-либо исключенияsetUp()
, JUnit напечатает очень полезную трассировку стека. С другой стороны, если во время построения объекта возникает исключение, в сообщении об ошибке просто говорится, что JUnit не смог создать экземпляр тестового примера, и вы не видите номер строки, в которой произошел сбой, вероятно, потому, что JUnit использует отражение для создания экземпляра теста. классы.Ничего из этого не применимо к примеру создания пустой коллекции, поскольку она никогда не будет выбрана, но это преимущество
setUp()
метода.источник
В дополнение к ответу Алекса Б.
Требуется даже использовать метод setUp для создания экземпляров ресурсов в определенном состоянии. Выполнение этого в конструкторе - это не только вопрос времени, но и из-за того, как JUnit запускает тесты, каждое состояние теста будет стерто после его запуска.
JUnit сначала создает экземпляры testClass для каждого метода тестирования и запускает тесты после создания каждого экземпляра. Перед запуском метода тестирования запускается метод его настройки, в котором можно подготовить некоторое состояние.
Если состояние базы данных будет создано в конструкторе, все экземпляры будут создавать экземпляры состояния базы данных сразу после друг друга перед запуском каждого теста. Что касается второго теста, тесты будут выполняться с грязным состоянием.
Жизненный цикл JUnits:
С некоторыми входами в тест с двумя методами тестирования вы получите: (число - это хэш-код)
источник
@BeforeClass
в JUnit 4.В JUnit 4:
@Before
метод для обнаружения сбоев.final
точно так, как указано в вопросе,@Before
для обнаружения сбоев.@BeforeClass
, но будьте осторожны с зависимостями между тестами.Инициализация
@Before
метода или метода тестирования позволяет улучшить отчеты об ошибках. Это особенно полезно для создания экземпляра тестируемого класса (который вы можете сломать), но также полезно для вызова внешних систем, таких как доступ к файловой системе («файл не найден») или подключение к базе данных («соединение отклонено»).Это приемлемо , чтобы иметь простой стандарт и всегда использовать
@Before
(явные ошибки , но многословно) или всегда инициализации в декларации (кратком , но дает ошибку заблуждения), так как сложные правила кодирования трудно следовать, и это не имеет большое значение .Инициализация в
setUp
- это пережиток JUnit 3, где все тестовые экземпляры были инициализированы с нетерпением, что вызывает проблемы (скорость, память, исчерпание ресурсов), если вы выполняете дорогостоящую инициализацию. Таким образом, лучше всего было выполнить дорогостоящую инициализацию вsetUp
, которая запускалась только при выполнении теста. Это больше не применяется, поэтому использовать его гораздо меньшеsetUp
.Это суммирует несколько других ответов, которые похоронили lede, в частности, Крейга П. Мотлина (сам вопрос и ответ сам), Moss Collum (тестируемый класс) и dsaff.
источник
В JUnit 3 ваши инициализаторы полей будут запускаться один раз для каждого метода тестирования перед запуском любых тестов. . Пока ваши значения полей невелики в памяти, занимают мало времени на настройку и не влияют на глобальное состояние, использование инициализаторов полей технически нормально. Однако, если они не выполняются, вы можете в конечном итоге потребить много памяти или времени на настройку полей до запуска первого теста и, возможно, даже нехватить памяти. По этой причине многие разработчики всегда устанавливают значения полей в методе setUp (), где это всегда безопасно, даже если это не является строго необходимым.
Обратите внимание, что в JUnit 4 инициализация тестового объекта происходит прямо перед запуском теста, поэтому использование инициализаторов полей является более безопасным и рекомендуемым стилем.
источник
В вашем случае (создание списка) на практике разницы нет. Но в целом лучше использовать setUp (), потому что это поможет Junit правильно сообщать об исключениях. Если в конструкторе / инициализаторе теста возникает исключение, это означает сбой теста . Однако, если во время установки возникает исключение, естественно думать об этом как о некоторой проблеме при настройке теста, и junit сообщает об этом соответствующим образом.
источник
Я предпочитаю в первую очередь удобочитаемость, которая чаще всего не использует метод настройки. Я делаю исключение, когда операция базовой настройки занимает много времени и повторяется в рамках каждого теста.
На этом этапе я перемещаю эту функциональность в метод настройки с помощью
@BeforeClass
аннотации (оптимизирую позже).Пример оптимизации с помощью
@BeforeClass
метода настройки: я использую dbunit для некоторых функциональных тестов базы данных. Метод установки отвечает за перевод базы данных в известное состояние (очень медленно ... 30 секунд - 2 минуты в зависимости от количества данных). Я загружаю эти данные в метод настройки, помеченный,@BeforeClass
а затем запускаю 10-20 тестов с тем же набором данных, а не повторно загружаю / инициализирую базу данных внутри каждого теста.Использование Junit 3.8 (расширение TestCase, как показано в вашем примере) требует написания немного большего количества кода, чем просто добавление аннотации, но «выполнить один раз перед установкой класса» все еще возможно.
источник
Поскольку каждый тест выполняется независимо, с новым экземпляром объекта, нет особого смысла в том, чтобы объект Test имел какое-либо внутреннее состояние, кроме общего между
setUp()
и отдельным тестом иtearDown()
. Это одна из причин (в дополнение к другим причинам) того, что использовать этотsetUp()
метод полезно .Примечание. Поддерживать статическое состояние тестовым объектом JUnit - плохая идея! Если вы используете статическую переменную в своих тестах для чего-либо, кроме целей отслеживания или диагностики, вы лишаете законной силы часть цели JUnit, заключающуюся в том, что тесты могут (а могут) выполняться в любом порядке, причем каждый тест выполняется с свежее, чистое состояние.
Преимущества использования заключаются в
setUp()
том, что вам не нужно вырезать и вставлять код инициализации в каждый тестовый метод и что у вас нет кода настройки теста в конструкторе. В вашем случае разница небольшая. Просто создать пустой список можно безопасно, пока вы его показываете или в конструкторе, поскольку это тривиальная инициализация. Однако, как вы и другие указали, все, что можетException
вызвать ошибку, должно быть сделано,setUp()
чтобы вы получили дамп диагностического стека в случае сбоя.В вашем случае, когда вы просто создаете пустой список, я бы сделал то же самое, что вы предлагаете: назначьте новый список в точке объявления. Тем более, что таким образом у вас есть возможность пометить его,
final
если это имеет смысл для вашего тестового класса.источник
final
в вопросе упоминается преимущество .Постоянные значения (используемые в фикстурах или утверждениях) должны быть инициализированы в их объявлениях и
final
(как никогда не изменяются)тестируемый объект должен быть инициализирован в методе настройки, потому что мы можем это настроить. Конечно, сейчас мы можем не устанавливать что-то, но мы можем установить это позже. Создание экземпляра метода init упростит внесение изменений.
зависимости тестируемого объекта, если они являются имитируемыми, даже не должны создаваться самостоятельно: сегодня фиктивные фреймворки могут создавать его экземпляры путем отражения.
Тест, не зависящий от имитации, может выглядеть так:
Тест с зависимостями, которые нужно изолировать, может выглядеть так:
источник