java test-2 JUnit 4&5 部分基础

ddatsh

dev #java
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-runner</artifactId>
    <scope>test</scope>
</dependency>

JUnit4和JUnit5在测试编码风格上没有太大变化

import org.junit.jupiter.api.*;

public class AppTest {

   @BeforeAll
   static void setup() {

      System.out.println("@BeforeAll executed");
   }

   @BeforeEach
   void setupThis() {

      System.out.println("@BeforeEach executed");
   }

   @Tag("DEV")
   @Test
   void testOne() {

   }

   @Tag("PROD")
   @Disabled
   @Test
   void testTwo() {

   }

   @AfterEach
   void tearThis() {

      System.out.println("@AfterEach executed");
   }

   @AfterAll
   static void tear() {

      System.out.println("@AfterAll executed");
   }
}

Assertion

org.junit.jupiter.Assertions

测试期望结果

assertEquals(expected, actual) 最常用

三方断言类库

JUnit Jupiter足以满足许多测试场景需要,更强大和附加功能,如匹配器,AssertJ,Hamcrest,Truth 等第三方断言库自由选择

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.Test;

@Test
void assertWithHamcrestMatcher() {
    assertThat(2 + 1, is(equalTo(3)));
}

JUnit 4 编程模型遗留继续用 org.junit.Assert.assertThat

assertThrows

@Test
void exceptionTesting() {
   Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
      throw new IllegalArgumentException("a message");
   });
   assertEquals("a message", exception.getMessage());
}

Fixture(生命周期相关)

用作 编写测试前准备、测试后清理的固定代码

@BeforeEach/@AfterEach和@BeforeAll/@AfterAll

  1. 实例变量,在 @BeforeEach 中初始化,在 @AfterEach中清理,它们在各个@Test方法中互不影响,因为是不同的实例
  2. 静态变量,在@BeforeAll中初始化,在@AfterAll中清理,它们在各个@Test方法中均是唯一实例,会影响各个@Test方法

大多用 @BeforeEach和 @AfterEach足够,只有某些测试资源初始化耗费时间太长,以至于不得不尽量“复用”时才用 @BeforeAll和 @AfterAll

condition

@Disabled

@EnabledOnOs(OS.WINDOWS)

@EnabledOnOs({ OS.LINUX, OS.MAC })

@DisabledOnOs(OS.WINDOWS)

@DisabledOnJre(JRE.JAVA_8)

@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")

@EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")

测试方法、测试类、测试集、测试运行器(Runners)

常见 Runners

JUnit 3 默认 JUnit4ClassRunner

JUnit4默认 BlockJUnit4ClassRunner

JUnit4 extends BlockJUnit4ClassRunner

@RunWith(JUnit4.class) 即调用默认JUnit 运行器

参数化测试

@ParameterizedTest

@ValueSource

@MethodSource

@CsvSource

@ParameterizedTest
@ValueSource(ints = { 0, 1, 5, 100 })
void testAbs(int x) {
    assertEquals(x, Math.abs(x));
}

public class StringUtils {
    public static String capitalize(String s) {
        if (s.length() == 0) {
            return s;
        }
        return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
    }
}

参数化测试,不但要给出输入,还要给出预期输出。测试方法至少需要接收两个参数:

@ParameterizedTest
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

@MethodSource,写个同名静态方法提供测试参数:

@ParameterizedTest
@MethodSource
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

static List<Arguments> testCapitalize() {
    return asList(
            Arguments.arguments("abc", "Abc"), 
            Arguments.arguments("APPLE", "Apple"), 
            Arguments.arguments("gooD", "Good"));
}
@CsvSource({ "abc, Abc", "APPLE, Apple", "gooD, Good" })
@CsvFileSource(resources = { "/test-capitalize.csv" })

Categories

public interface FastTests { /* category marker */ }
public interface SlowTests { /* category marker */ }

public class A {
  @Test
  public void a() {
    fail();
  }

  @Category(SlowTests.class)
  @Test
  public void b() {
  }
}

@Category({SlowTests.class, FastTests.class})
public class B {
  @Test
  public void c() {

  }
}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses( { A.class, B.class })
public class SlowTestSuite {
  // Will run A.b and B.c, but not A.a
}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@ExcludeCategory(FastTests.class)
@SuiteClasses( { A.class, B.class }) 
public class SlowTestSuite {
  // Will run A.b, but not A.a or B.c
}

测试套件

@RunWith(JUnitPlatform.class)
@SelectPackages("junit5.examples")
public class JUnit5TestSuiteExample {
}

过滤测试包、类甚至测试方法

  1. @IncludePackages@ExcludePackages来过滤包
  2. @IncludeClassNamePatterns@ExcludeClassNamePatterns过滤测试类
  3. @IncludeTags@ExcludeTags过滤测试方法
@RunWith(JUnitPlatform.class)
@SelectPackages("junit5.examples")
@IncludePackages("junit5.examples.packageA")
@ExcludeTags("PROD")
public class JUnit5TestSuiteExample {
}

maven FailSafe & Surefire

maven的生命周期与集成测试相关的四个阶段

1.pre-integration-test:准备集成测试环境,类似于junit单元测试中的setUp

2.integration-test:执行集成测试

3.post-integration-test:销毁集成测试的环境,类似于junit单元测试中的tearDown

4.校验:分析集成测试的结果

FailSafe工作在integration-test以及verify阶段,与surefire插件不同的是不会因为集成测试中失败而终止整个过程,即post-integration-test可以确定执行

FailSafe插件有两个goal:integration-test 和verify

1、3 如通过容器插件,jetty/tomcat plugin 或cargo plugin实现

2、4由failsafe实现

cargo可加载命令行参数,通过jacoco统计测试覆盖率,tomcat plugin就无法做到

 <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-failsafe-plugin</artifactId>
     <executions>
         <execution>
             <id>integration-test</id>
             <goals>
                 <goal>integration-test</goal>
             </goals>
         </execution>
         <execution>
             <id>verify</id>
             <goals>
                 <goal>verify</goal>
             </goals>
         </execution>
     </executions>
</plugin>

IT用例如果和UT用例放在同项目,必须在在UT的surefire中exclude所有的IT用例

spring boot service注入

junit5

@ExtendWith(SpringExtension.class) 

junit4

@RunWith(SpringRunner.class)

不加,service无法注入,值为null

ref

https://www.cnblogs.com/felixzh/p/12554701.html

https://zhuanlan.zhihu.com/p/162032557

https://www.baeldung.com/junit-5-migration

https://blog.csdn.net/HeatDeath/article/details/79841526

https://www.cnblogs.com/lspz/p/6727123.html

http://www.jfh.com/jfperiodical/article/1455?

https://doczhcn.gitbook.io/junit5/index/index