Правила

Правила (Rules) JUnit позволяют влиять на поведение и исполнение всех тестов в классе. Правило представляет собой код, исполняющийся «вокруг» тестового метода, для каждого такого метода, позволяя расширять и переделывать поведение JUnit каким угодно образом.

Собственное правило

Возьмём код из примера с предположениями и перепишем его с использованием правил. С помощью правил мы будем выводить на консоль текущую системную локаль.

Все классы правил реализуют интерфейс TestRule  и его метод apply, который принимает завёрнутый в Statement  тест и его описание в Description  и возвращает Statement. Причём это может быть любой Statement, например с другим тестом внутри или вообще без теста. В примере я возвращаю Statement, который выводит значение языка для теста и исполняет сам тест.

Имзенение кода теста, для использования нового правила, тоже незначительное:

Запуск этого теста порадует нас строчками в логе:

Заготовки

JUnit предоставляет два базовых класса облегчающих написание собствнных правил: ExternalResource  и TestWatcher.

ExternalResource

ExternalResource реализует @Before/@After парадигму — позволяет проводить какие-либо действия до вызова класса и после его вызова. Например, перепишем тест для StringUtils из статьи по основам JUnit с использованием ExternalResource:

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

Само по себе использование класса ExternalResource  вполне очевидно — метод before()  вызывает перед вызовом каждого теста, метод after()  — после. Однако, если правило имеет аннотацию @ClassRule, то методы before()/after()  будут вызваны однократно, для всего класса. Действие @ClassRule, разумеется, распространяется на все правила .

TestWatcher

Базовый класс для правил, которые участвуют в исполнении тестов, но не вмешиваются в сам процесс тестирования. Например правило, записывающее текущую локаль теста, может быть переписано с использованием TestWatcher:

Применим оба правила вместе:

RuleChain позволяет объединять несколько правил в цепочкe правил с заданным порядком выполнения. Если бы я написал:

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

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

Правила, поставляемые с JUnit

JUnit содержит несколько заранее написанных правил:

  • TemporaryFolder
  • TestName
  • ErrorCollector
  • Verifier
  • ExpectedException
  • Timeout

TemporaryFolder

Предоставляет временную директорию для файлов, которая будет очищена после завершения теста (пример из официальной документации JUnit):

TestName

Позволяет тестам узнавать собственное имя:

ErrorCollector

Собирает ошибки, возникающие в ходе выпонения теста и проваливает весь тест целиком. Полезен, когда проверяется поведение при каких-то независимых условиях и хочется проверить всё сразу:

Если при выполнении теста возникнут ошибки, они будут записаны, а тест продолжит своё выполнение. Список ошибок будет выведен после завершения теста.

Verifier

Базовый класс для ErrorCollectior , позволяет провалить успешный тест, если он не проходит проверку после выполнения (пример из официальной документации JUnit):

ExpectedException

Улучшает поддержку исключений в JUnit. Это правило подробно рассмотрено в статье о тестировании исключений.

Помимо описанного там обычно применения, можно использовать это правило совместое с теориями, для перехвата исключений в них (хотя отсуствие expectedException у аннотации @Theory  как бы говорит нам, что так лучше не делать):

Timeout

Этому правилу посвящена отдельная статья.

Код примеров на github, отдельно для предположений и теорий и отдельно для основных возможностей.