Во время работы над моим текущим проектом у меня появилось некоторое время для перехода с JUnit 4 на JUnit 5 . Поскольку JUnit 5 был выпущен в сентябре 2017 года, сейчас самое подходящее время взглянуть на него.
Мое приложение представляет собой проект java 8 maven, разделенный на 7 модулей maven, и каждый модуль имеет собственные интеграционные и модульные тесты. Однако один из этих модулей посвящен тестам. Он содержит все необходимые для тестирования зависимости и вводится как тест области видимости в другие модули. Наши тестовые зависимости являются наиболее распространенными в Java-проекте. Мы используем JUnit 4, AssertJ , Мокито , DBUnit и Весенний тест .
Наконец, у нас также есть специальный проект для выполнения сквозных тестов на основе Selenium , Флюентлениум и Дано . К сожалению, Given не полностью поддерживает JUnit 5. В настоящее время он находится в экспериментальном состоянии , поэтому я еще не начал эту миграцию.
Зависимости
Давайте начнем с добавления новых зависимостей JUnit:
org.junit.jupiter junit-jupiter-engine ${junit.version} org.junit.vintage junit-vintage-engine ${junit.version} org.junit.platform junit-platform-launcher ${junit.platform.version} org.junit.platform junit-platform-runner ${junit.platform.version}
Важно обратить внимание на импорт junit-vintage-engine
. Он предоставляет возможность без труда запускать тесты JUnit 4 и JUnit 5 одновременно.
Модульные тесты
Следующим шагом будет замена всего импорта старых аннотаций JUnit на самые новые.
import org.junit.Test
становиться
import org.junit.jupiter.api.Test;
Вот отображение каждой аннотации:
org.junit. До | org.junit.jupiter.api. Перед Каждым |
org.junit. После | org.junit.jupiter.api. После |
org.junit. Перед занятием | org.junit.jupiter.api. Перед всеми |
org.junit. Послеклассный | org.junit.jupiter.api. В конце концов |
org.junit. Игнорировать | org.junit.jupiter.api. Нетрудоспособный |
Поскольку мы используем AssertJ для всех наших утверждений, мне не нужно было переносить утверждения JUnit4.
Правила
Одним из самых больших изменений является удаление концепции rules , которая была заменена моделью расширения . Целью расширения является расширение поведения тестовых классов или методов, и оно заменяет JUnit runner и Junit Rules.
Одно правило, которое мы все использовали, – это ExpectedException
и он может быть легко заменен JUnit assertThrows
:
@Test void exceptionTesting() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { throw new IllegalArgumentException("a message"); }); assertEquals("a message", exception.getMessage()); }
Другим хорошо известным правилом для миграции является Временная папка
. К сожалению, JUnit 5 пока не предоставляет замену. В Github есть открытый issue .
Обзор
См. Обсуждение по адресу https://github.com/junit-team/junit5-samples/issues/4 .
Связанные с этим Вопросы
- #219
Результаты
- [X]
Представить официальное лицоВременная папка
расширение для JUnit Jupiter аналогично поддержке правил в JUnit 4.
Итак, что мы можем сделать, чтобы это сработало?
Прежде всего, в JUnit 4 можно поддерживать тесты с использованием этих правил благодаря junit-vintage-engine
.
Другим решением является продолжение использования JUnit 4 Временная папка
правьте , добавляя зависимость junit-jupiter-поддержка миграции
.
org.junit.jupiter junit-jupiter-migrationsupport ${junit.version}
Этот модуль позволяет запускать тесты JUnit 5 с правилами. Например:
@EnableRuleMigrationSupport public class JUnit4TemporaryFolderTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void test() throws IOException { temporaryFolder.newFile("new_file"); } }
Однако эта функция поддерживает только:
- правила, расширяющие
org.junit.rules. Внешний Ресурс
- правила, расширяющие
org.junit.rules. Верификатор
- правило
Ожидаемое исключение
и в настоящее время он помечен как экспериментальный , так что используйте его на свой страх и риск.
Наконец, одно из решений – создать свой собственный Расширение временной папки
основано на реализации Junit 4.
public class TemporaryFolderExtension implements BeforeEachCallback, AfterEachCallback { private final File parentFolder; private File folder; public TemporaryFolderExtension() { this(null); } public TemporaryFolderExtension(File parentFolder) { this.parentFolder = parentFolder; } @Override public void afterEach(ExtensionContext extensionContext) { if (folder != null) { recursiveDelete(folder); } } @Override public void beforeEach(ExtensionContext extensionContext) throws IOException { folder = File.createTempFile("junit", "", parentFolder); folder.delete(); folder.mkdir(); } public File newFile(String fileName) throws IOException { File file = new File(getRoot(), fileName); if (!file.createNewFile()) { throw new IOException("a file with the name \'" + fileName + "\' already exists in the test folder"); } return file; } public File newFolder(String folderName) { File file = getRoot(); file = new File(file, folderName); file.mkdir(); return file; } private void recursiveDelete(File file) { File[] files = file.listFiles(); if (files != null) { for (File each : files) { recursiveDelete(each); } } file.delete(); } public File getRoot() { if (folder == null) { throw new IllegalStateException("the temporary folder has not yet been created"); } return folder; } }
Эта реализация не полностью поддерживает все функции расширения, такие как Разрешение параметров но, по крайней мере, это позволяет нам полностью перенести наши тесты на JUnit 5. Кроме того, можно вводить расширения как правило, используя @RegisterExtension
@RegisterExtension public TemporaryFolderExtension temporaryFolder = new TemporaryFolderExtension();
Эта аннотация позволяет нам создать расширение с параметрами и получить доступ к нему во время выполнения теста.
Пользовательские Правила
В моем случае у меня было только одно пользовательское правило для переноса. Его цель – создать SMTP-сервер в памяти для подтверждения отправки электронных писем.
public class SMTPServerRule extends ExternalResource { private GreenMail smtpServer; private String hostname; private int port; public SMTPServerRule() { this(25); } public SMTPServerRule(int port) { this("localhost", port); } public SMTPServerRule(String hostname, int port) { this.hostname = hostname; this.port = port; } @Override protected void before() throws Throwable { super.before(); smtpServer = new GreenMail(new ServerSetup(port, hostname, "smtp")); smtpServer.start(); } public ListgetMessages() { return Lists.newArrayList(smtpServer.getReceivedMessages()).stream() .parallel() .map(mimeMessage -> ExpectedMail.transformMimeMessage(mimeMessage)).collect(Collectors.toList()); } @Override protected void after() { super.after(); smtpServer.stop(); } }
Чтобы заставить его работать как расширение JUnit, ему нужно только реализовать Перед каждым обратным вызовом
и |/После каждого обратного вызова интерфейсы вместо наследования от/| Внешний ресурс
. Основная реализация остается прежней.
public class SMTPServerExtension implements BeforeEachCallback, AfterEachCallback { private GreenMail smtpServer; private String hostname; private int port; public SMTPServerExtension() { this(25); } public SMTPServerExtension(int port) { this("localhost", port); } public SMTPServerExtension(String hostname, int port) { this.hostname = hostname; this.port = port; } public ListgetMessages() { return Lists.newArrayList(smtpServer.getReceivedMessages()).stream() .parallel() .map(mimeMessage -> ExpectedMail.transformMimeMessage(mimeMessage)).collect(Collectors.toList()); } @Override public void afterEach(ExtensionContext extensionContext) throws Exception { smtpServer.stop(); } @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { smtpServer = new GreenMail(new ServerSetup(port, hostname, "smtp")); smtpServer.start(); }
Интеграционные тесты
Интеграционные тесты Удлинитель пружины
входит в состав пружины 5.
@RunWith(SpringJUnit4ClassRunner.class)
становиться
@ExtendWith(SpringExtension.class)
Тесты Mockito
Давайте продолжим с тестами, использующими Mockito. Как мы уже делали с интеграционными тестами Spring, мы должны зарегистрировать расширение:
@RunWith(MockitoJUnitRunner.class)
становиться
@ExtendWith(MockitoExtension.class)
На самом деле, класс Расширение Mockito
еще не предоставлено Mockito и это будет введено с Mockito 3. Одно из решений такое же, как Расширение временной папки
… это делается для того, чтобы сохранить наши тесты в JUnit 4. Тем не менее, также возможно создать свое собственное расширение, и поэтому команда Junit предоставляет одну реализацию Mockito Extension
в своих примерах. Я решил импортировать его в свой проект, чтобы завершить миграцию.
Удалить JUnit 4
Затем, чтобы убедиться, что все мои тесты выполняются под JUnit 5, я проверил, есть ли какая-либо зависимость от JUnit4, выполнив:
mvn dependency:tree
И поэтому мне пришлось исключить некоторые из них:
org.springframework.boot spring-boot-starter-test junit junit org.dbunit dbunit ${dbunit.version} junit junit
Знаток
И последнее, но не менее важное: мне нужно было обновить плагин maven surefire, чтобы он работал с JUnit 5.
org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.junit.platform junit-platform-surefire-provider 1.1.1 org.junit.jupiter junit-jupiter-engine ${junit.version}
Будьте осторожны с версией вашего плагина maven surefire, так как 2.20
имеет утечку памяти . Документация JUnit предлагает версию 2.21
.
Вывод
Эта миграция была действительно простой, но даже в этом случае JUnit 5 полностью отличается от JUnit 4. В конце концов, я смог удалить импорт junit-vintage-engine
, так как у меня больше нет теста Junit 4. Я сожалею только о том факте, что мне пришлось создать свое собственное расширение временной папки и расширение Mockito. Наконец, можно получить дополнительную помощь в вашей миграции, обратившись к Junit5-samples .
Большое спасибо Sony th , Микаэлю и Houssem за их время и корректуру.
Оригинал: “https://dev.to/vga/migration-from-junit-4-to-junit-5-19d6”