setUp / tearDown (@ Before / @ After) зачем они нужны в JUnit? - PullRequest
60 голосов
/ 06 сентября 2010

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

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

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

Ответы [ 6 ]

58 голосов
/ 06 сентября 2010

В этой (старой) рекомендациях JUnit в статье говорится:

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

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

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) и отсюда. Это делает это намного проще объяснить неудачу установки теста.

24 голосов
/ 07 сентября 2010

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

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

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

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

23 голосов
/ 06 сентября 2010

Вот 3 веские причины, почему. В итоге:

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

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

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

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

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

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

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

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

Экстремальное программирование на Java в ETutorial - 4.6 Настройка и устранение
http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/

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

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

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

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

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

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

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

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

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

6 голосов
/ 06 сентября 2010

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

3 голосов
/ 06 сентября 2010

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

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

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

0 голосов
/ 18 сентября 2010

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

  1. Если вы переместите содержимое @Before в Constructor, это нормально, но содержание @After, куда вы переместитесь?
  2. Различия между Конструктором и @ Before / @ After заключаются в том, что Конструктор должен использоваться для экземпляра некоторого класса, @ Before / @ After - для подготовки ресурсов тестового примера.
...