Руководство по JUnit5

Учебник JUnit5

В этом учебнике по JUnit мы познакомимся с основами JUnit5 и его новыми возможностями на примерах. В мире Java JUnit является одним из популярных фреймворков, используемых для написания модульных тестов на Java. JUnit в основном помогает разработчикам тестировать свой код на JVM самостоятельно.

Архитектура JUnit5

Платформа JUnit

  • Запускает тестовые фреймворки на JVM
  • Имеет API TestEngine, используемый для создания тестового фреймворка, который работает на платформе JUnit

JUnit Jupiter

  • Сочетание новой модели программирования для написания тестов и модели расширений для расширений
  • Добавление новых аннотаций, таких как @BeforeEach, @AfterEach, @AfterAll, @BeforeAll и т. д.

JUnit Vintage

  • Предоставляет поддержку для выполнения предыдущих тестов JUnit версии 3 и 4 на этой новой платформе

Зависимости JUnit Maven

Для реализации тестов на основе JUnit5 в проекте добавьте следующую зависимость в файл pom.xml проекта:

  • Библиотека JUnit 5
<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-engine</artifactId>
     <version>5.1.1</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.junit.platform</groupId>
     <artifactId>junit-platform-runner</artifactId>
     <version> 1.1.1</version>
     <scope>test</scope>
</dependency>
  • Поставщик maven surefire для выполнения модульных тестов, если среда разработки не поддерживает JUnit5 (если среда разработки поддерживает, то этот пункт не требуется)
<plugin>
     <artifactId>maven-surefire-plugin</artifactId>
     <version>2.19.1</version>
     <dependencies>
          <dependency>
               <groupId>org.junit.platform</groupId>
               <artifactId>junit-platform-surefire-provider</artifactId>
               <version>1.0.2</version>
          </dependency>
     </dependencies>
</plugin>

Новые функции JUnit5

Требуется Java 8 или выше при выполнении. Но всё ещё можно тестировать код, скомпилированный с использованием предыдущих версий Java. В нём было введено множество новых функций.

Аннотации JUnit

Ниже приведены некоторые часто используемые аннотации, предоставленные в нем:

Annotation Description
@Test Denotes a test method
@DisplayName Declares a custom display name for the test class or test method
@BeforeEach Denotes that the annotated method should be executed before each test method
@AfterEach Denotes that the annotated method should be executed after each test method
@BeforeAll Denotes that the annotated method should be executed before all test methods
@AfterAll Denotes that the annotated method should be executed after all test methods
@Disable Used to disable a test class or test method
@Nested Denotes that the annotated class is a nested, non-static test class
@Tag Declare tags for filtering tests
@ExtendWith Register custom extensions
package com.journaldev;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JUnit5Sample1Test {

  @BeforeAll
  static void beforeAll() {
    System.out.println("**--- Executed once before all test methods in this class ---**");
  }

  @BeforeEach
  void beforeEach() {
    System.out.println("**--- Executed before each test method in this class ---**");
  }

  @Test
  void testMethod1() {
    System.out.println("**--- Test method1 executed ---**");
  }

  @DisplayName("Test method2 with condition")
  @Test
  void testMethod2() {
    System.out.println("**--- Test method2 executed ---**");
  }

  @Test
  @Disabled("implementation pending")
  void testMethod3() {
	  System.out.println("**--- Test method3 executed ---**");
  }

  @AfterEach
  void afterEach() {
    System.out.println("**--- Executed after each test method in this class ---**");
  }

  @AfterAll
  static void afterAll() {
    System.out.println("**--- Executed once after all test methods in this class ---**");
  }


}

Мы можем запустить вышеуказанный класс теста JUnit в Eclipse -> Run As -> JUnit Test.

Утверждения JUnit

Каждый метод тестирования должен быть оценен по условию как true с использованием утверждений, чтобы тест мог продолжить выполнение. Утверждения JUnit Jupiter находятся в классе org.junit.jupiter.api.Assertions. Все методы являются статическими.

Assertion Description
assertEquals(expected, actual) Fails when expected does not equal actual
assertFalse(expression) Fails when expression is not false
assertNull(actual) Fails when actual is not null
assertNotNull(actual) Fails when actual is null
assertAll() Group many assertions and every assertion is executed even if one or more of them fails
assertTrue(expression) Fails if expression is not true
assertThrows() Class to be tested is expected to throw an exception
@Test
void testAssertEqual() {
	 assertEquals("ABC", "ABC");
	 assertEquals(20, 20, "optional assertion message");
	 assertEquals(2 + 2, 4);
}

@Test
void testAssertFalse() {
	 assertFalse("FirstName".length() == 10);
	 assertFalse(10 > 20, "assertion message");
}

@Test
void testAssertNull() {
     String str1 = null;
	 String str2 = "abc";
	 assertNull(str1);
	 assertNotNull(str2);	
}

@Test
void testAssertAll() {
	 String str1 = "abc";
	 String str2 = "pqr";
	 String str3 = "xyz";
	 assertAll("numbers",
	      () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr"),
		  () -> assertEquals(str3,"xyz")
	 );
	 //раскомментируйте нижеприведенный код и понимайте каждое выполнение утверждения
     /*assertAll("numbers",
		  () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr1"),
		  () -> assertEquals(str3,"xyz1")
	 );*/
}

@Test
void testAssertTrue() {
	 assertTrue("FirstName".startsWith("F"));
	 assertTrue(10  {
	      throw new IllegalArgumentException("Illegal Argument Exception occured");
	 });
	 assertEquals("Illegal Argument Exception occured", exception.getMessage());
}

Импорты JUnit5

Его тестовые классы нуждаются в импорте org.junit.jupiter.api.Test, а не в org.junit.Test. Кроме того, тестовые методы не обязательно должны быть публичными и находиться в локальном пакете.

import org.junit.jupiter.api.Test;

Предположения JUnit5

Предположения – это статические методы в классе org.junit.jupiter.api.Assumptions. Они будут выполнять тест только тогда, когда указанное условие будет выполнено, в противном случае тест будет прерван. Прерванный тест не вызовет сбой сборки. Когда предположение не выполняется, выбрасывается исключение org.opentest4j.TestAbortedException, и тест пропускается.

Assumptions Description
assumeTrue Execute the body of lamda when the positive condition hold else test will be skipped
assumeFalse Execute the body of lamda when the negative condition hold else test will be skipped
assumingThat Portion of the test method will execute if an assumption holds true and everything after the lambda will execute irrespective of the assumption in assumingThat() holds
@Test
void testAssumeTrue() {
     boolean b = 'A' == 'A';
     assumeTrue(b);
     assertEquals("Hello", "Hello");
}

@Test
@DisplayName("test executes only on Saturday")
public void testAssumeTrueSaturday() {
     LocalDateTime dt = LocalDateTime.now();
     assumeTrue(dt.getDayOfWeek().getValue() == 6);
     System.out.println("further code will execute only if above assumption holds true");
}

@Test
void testAssumeFalse() {
     boolean b = 'A' != 'A';
     assumeFalse(b);
     assertEquals("Hello", "Hello");
}

@Test
void testAssumeFalseEnvProp() {
     System.setProperty("env", "prod");
     assumeFalse("dev".equals(System.getProperty("env")));
     System.out.println("further code will execute only if above assumption hold");
}

@Test
void testAssumingThat() {
     System.setProperty("env", "test");
     assumingThat("test".equals(System.getProperty("env")),
          () -> {
               assertEquals(10, 10);
               System.out.println("perform below assertions only on the test env");
               });

     assertEquals(20, 20);
     System.out.println("perform below assertions on all env");
}

Вложенные тестовые классы JUnit

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

@BeforeAll
static void beforeAll() {
     System.out.println("**--- JUnit5Sample4Test :: beforeAll :: Executed once before all test methods ---**");
}
 
@BeforeEach
void beforeEach() {
	 System.out.println("**--- JUnit5Sample4Test :: beforeEach :: Executed before each test method ---**");
}

@AfterEach
void afterEach() {
	 System.out.println("**--- JUnit5Sample4Test :: afterEach :: Executed after each test method ---**");
}

@AfterAll
static void afterAll() {
	 System.out.println("**--- JUnit5Sample4Test :: afterAll :: Executed after all test method ---**");
}
 
     @Nested
     class InnerClass {
 
          @BeforeEach
          void beforeEach() {
               System.out.println("**--- InnerClass :: beforeEach :: Executed before each test method ---**");
          }
 
          @AfterEach
          void afterEach() {
        	   System.out.println("**--- InnerClass :: afterEach :: Executed after each test method ---**");
          }
 
          @Test
          void testMethod1() {
        	   System.out.println("**--- InnerClass :: testMethod1 :: Executed test method1 ---**");
          }
 
          @Nested
          class InnerMostClass {
 
               @BeforeEach
               void beforeEach() {
                    System.out.println("**--- InnerMostClass :: beforeEach :: Executed before each test method ---**");
               }
 
               @AfterEach
               void afterEach() {
            	    System.out.println("**--- InnerMostClass :: afterEach :: Executed after each test method ---**");
               }
 
               @Test
               void testMethod2() {
            	    System.out.println("**--- InnerMostClass :: testMethod2 :: Executed test method2 ---**");
               }
        }
    }

Исключение теста JUnit

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

Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
     throw new IllegalArgumentException("Illegal Argument Exception occured");
});
assertEquals("Illegal Argument Exception occured", exception.getMessage());

Выполнение тестов JUnit

Модульные тесты могут быть выполнены несколькими способами, два из которых следующие:

  • Используйте среду Eclipse IDE Oxygen.3a (4.7.3a) и откройте файл теста для выполнения. Щелкните правой кнопкой мыши по файлу и выберите опцию “Запуск как”, а затем “Тест JUnit”
  • Используйте команду mvn test в командной строке Windows

Резюме

Мы рассмотрели JUnit5 и его новые возможности на примерах. Мы также изучили, как мы можем использовать аннотации, утверждения, предположения, исключения и писать вложенные тестовые классы в JUnit.

Вы можете скачать полный пример проекта из нашего репозитория на GitHub.

Source:
https://www.digitalocean.com/community/tutorials/junit5-tutorial