java test-2 JUnit 4&5 部分基础
ddatsh
<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)
最常用
- assertTrue()
- assertFalse()
- assertNotNull()
- assertArrayEquals()
- …
三方断言类库
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
- 实例变量,在 @BeforeEach 中初始化,在 @AfterEach中清理,它们在各个@Test方法中互不影响,因为是不同的实例
- 静态变量,在@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)
- 测试方法就是用@Test注解的一些函数
- 测试类是包含一个或多个测试方法的一个 **Test.java文件
- 测试集是一个suite,可能包含多个测试类
- 测试运行器则决定了用什么方式偏好去运行这些测试集/类/方法
常见 Runners
-
@RunWith(Parameterized.class) 配合@Parameters使用JUnit的参数化功能
-
@RunWith(Suite.class)
@SuiteClasses({ATest.class,BTest.class,CTest.class})
-
@RunWith(JUnit4.class) junit4的默认运行器
-
@RunWith(JUnit38ClassRunner.class),兼容junit3.8
-
@RunWith(SpringJUnit4ClassRunner.class)集成了spring的一些功能
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 {
}
过滤测试包、类甚至测试方法
@IncludePackages
和@ExcludePackages
来过滤包@IncludeClassNamePatterns
和@ExcludeClassNamePatterns
过滤测试类@IncludeTags
和@ExcludeTags
过滤测试方法
@RunWith(JUnitPlatform.class)
@SelectPackages("junit5.examples")
@IncludePackages("junit5.examples.packageA")
@ExcludeTags("PROD")
public class JUnit5TestSuiteExample {
}
maven FailSafe & Surefire
-
Surefire插件用来执行单元测试
-
FailSafe插件用来执行集成测试
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