SetUp/tearDown (@Before/@After), почему мы нуждаемся в них в JUnit?

Я считаю, что мы все знаем, что setUp (@Before) будет выполняться до того, как любой тестовый метод и tearDown (@After) будут выполняться после тестового метода.

Также мы знаем, что Junit создаст один экземпляр Test для каждого тестового метода.

Мой вопрос в том, что мы можем просто переместить содержимое метода setUp в класс Constructor и удалить метод setUp? есть ли какая-либо конкретная причина для сохранения метода setUp?

Ответ 1

Этот (старый) JUnit best practices гласит следующее:

Не используйте конструктор тестового случая для настройки тестового примера

Настройка тестового примера в конструктор - не очень хорошая идея. Рассмотрим:

public class SomeTest extends TestCase
   public SomeTest (String testName) {
      super (testName);
      // Perform test set-up
   }
}

Представьте, что при выполнении настройки, код настройки генерирует IllegalStateException. В ответ, Юнит бросил бы AssertionFailedError, что указывает на то, что тестовый пример не может быть инстанцирован. Вот пример полученная трассировка стека:

junit.framework.AssertionFailedError: Cannot instantiate test case: test1   
    at junit.framework.Assert.fail(Assert.java:143)
    at junit.framework.TestSuite.runTest(TestSuite.java:178)
    at junit.framework.TestCase.runBare(TestCase.java:129)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    at junit.framework.TestCase.run(TestCase.java:120)
    at junit.framework.TestSuite.run(TestSuite.java, Compiled Code)
    at junit.ui.TestRunner2.run(TestRunner.java:429)

Эта трассировка стека доказывает неинформативные; это только указывает на то, что тестовый пример не может быть инстанцирован. Он не детализирует исходное местоположение ошибки или место происхождение. Это отсутствие информации делает трудно вывести исключение основная причина.

Вместо настройки данных в конструктор, выполнить тестовую настройку переопределение setUp(). Любое исключение брошенное в пределах setUp()правильно. Сравните трассировку стека с предыдущим примером:

java.lang.IllegalStateException: Oops
    at bp.DTC.setUp(DTC.java:34) 
    at junit.framework.TestCase.runBare(TestCase.java:127)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    ...

Эта трассировка стека намного больше информативный; он показывает, какое исключение был выброшен (IllegalStateException) и откуда. Это значительно упрощает для объяснения отказа тестовой установки.

Ответ 2

На работе мы обнаружили нечто довольно интересное, которое отвечает на ваш вопрос. Когда вы запускаете тестовый набор, особенно большой набор тестов (200+), JUnit начинает использовать много памяти, это связано с тем, что ВСЕ тесты установлены до запуска любого фактического метода тестирования.

Мы столкнулись с "утечкой памяти" из-за этого, потому что мы использовали Spring для подключения некоторых объектов JPA EntiryManager для наших тестов базы данных, это стало большим количеством объектов и большой объем памяти и примерно половину пути тестирования мы получали исключения OutOfMemory.

IMHO, лучше всего использовать setUp и tearDown, чтобы вводить ваши зависимости и обнулять любые ссылки на все классы, это заставит ваши тесты работать быстрее и сэкономить вам много головной боли!

Надеюсь, вы узнаете из наших ошибок:)

Ответ 3

Вот 3 веские причины. Вкратце:

  • Некоторые ситуации могут предпочесть отложить настройку тестовых приборов как можно дольше, до того, как будет выполняться тестовый сценарий.

  • Некоторые тестовые примеры могут быть частью иерархии наследования с глубоким тестированием. Может быть предпочтительнее отложить установку тестовых приборов до завершения полной иерархии конструкторов.

  • Вы получите лучшую диагностику, если код установки завершится с ошибкой в ​​setUp(), а не в случае сбоя в конструкторе.

1. Отложите настройку приборов до момента тестирования

Дизайн для удобства использования http://www.artima.com/weblogs/viewpost.jsp?thread=70189

... И, как выразился Эллиотт Расти Гарольд, если вы собираетесь создать новый экземпляр TestCase для каждого тестового метода, "зачем, черт возьми, с помощью метода setUp()?" Вы можете просто использовать конструктор TestCase.

Я слышал, как Брюс Эккел заметил, что существует одно тонкое различие между созданием вашего прибора в setUp() и его созданием в конструкторе TestCase. JUnit создает все экземпляры TestCase спереди, а затем для каждого экземпляра вызывает setup(), метод тестирования и tearDown(). Другими словами, тонкая разница заключается в том, что все конструкторы вызываются в пакетном представлении спереди, тогда как метод setUp() вызывается непосредственно перед каждым тестовым методом. Но это, по-видимому, не так уж и полезно, как на практике.

2. Отложить настройку приборов до тех пор, пока не будут созданы все тестовые примеры

ETutorial Java Extreme Programming - 4.6 Настройка и срыв

http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/

Возможно, вам интересно, почему вы должны написать метод setUp() вместо простого инициализации полей в конструкторе тестового случая. В конце концов, поскольку новый экземпляр тестового примера создается для каждого из его тестовых методов, конструктор всегда вызывается перед setUp(). В подавляющем большинстве случаев вы можете использовать конструктор вместо setUp() без каких-либо побочных эффектов.

В случаях, когда ваш тестовый пример является частью более глубокой иерархии наследования, вы можете отложить инициализацию объекта до тех пор, пока экземпляры производных [тестовых] классов не будут полностью построены. Это хорошая техническая причина, по которой вы можете использовать setUp() вместо конструктора для инициализации. Использование setUp() и tearDown() также полезно для целей документации, просто потому, что это может облегчить чтение кода.

3. Улучшенная диагностика в случае сбоя установки

Лучшие практики JUnit (JavaWorld)
http://www.javaworld.com/jw-12-2000/jw-1221-junit.html

Настройка тестового примера в конструкторе - не очень хорошая идея....

Представьте [в коде, где настройка выполняется в конструкторе тестового случая], что при выполнении настройки код установки выдает исключение IllegalStateException. В ответ JUnit выкинет AssertionFailedError, указав, что тестовый пример не может быть создан....

Эта трассировка стека [исключения, созданного в установочном коде в конструкторе тестового случая], оказывается довольно неинформативной; это только указывает на то, что тестовый пример не может быть создан.

Вместо настройки данных в конструкторе выполнить тестовую настройку, переопределив setUp(). Любое исключение, отправленное в setUp(), сообщается правильно....

Эта трассировка стека [исключения, созданного в методе setUp() вместо конструктора тестового случая], гораздо более информативна; он показывает, какое исключение было выбрано (IllegalStateException) и откуда. Это значительно облегчает объяснение отказа тестовой установки.

Ответ 4

Пользовательскому бегуну, например SpringJUnit4ClassRunner, возможно, потребуется выполнить некоторые коды между конструктором и @Before. В этом случае бегун может вводить некоторую зависимость, которой нужны методы @Before. Но инъекция зависимостей может быть запущена только после того, как объект будет создан.

Ответ 5

Причина, по которой вам нужно, это то, что для многих тестов вам часто нужно инициализировать состояние перед каждым тестом, чтобы тесты могли делать предположения о состоянии запуска, в котором они работают.

Предположим, что ваш тестовый класс обертывает, скажем, доступ к базе данных. После каждого теста вы хотите удалить все изменения, сделанные вашими испытаниями в db, - если вы этого не сделали, каждый тест выполняется против слегка измененной базы данных. Кроме того, любой заданный тест может увидеть другой набор изменений, если некоторые подмножества предыдущих тестов не удались. Например, предположим, что test1 выполняет вставку, test2 проверяет, что вы точно читаете размер таблицы. День 1, тест1 терпит неудачу, а 0 - правильно. День 2, тест1 преуспевает, а 1 правильный?

BTW, junit также поддерживает @BeforeClass, если вы хотите выполнить глобальную настройку, а настройка и отключение являются необязательными.

Ответ 6

Я думаю, что некоторая причина должна понравиться:

  • Если вы перемещаете содержимое @Before в конструктор, это прекрасно, но содержимое @After, куда вы можете перейти?
  • Различия конструктора и @Before/@After заключаются в том, что конструктор должен использоваться для экземпляра some для класса, @Before/@After предназначен для подготовки ресурсов тестового случая.