目录
介绍:
JUnit5=JUnit platform(测试框架基础)+JUnit Jupiter(新版本的,包含测试引擎)+JUnit Vintage(老版本的编程模型)
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
如果你需要用JUnit4的话需要导入以下依赖:
<dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency>
以前:@SpringBootTest+@RunWith(SpringTest.class)
现在: 编写测试方法我们用@Test即可,如果需要Spring功能,@SpringBootTest,可以使用@AutoWired,@Transactional...
常用注解:
@Test:表示方法为测试方法,职责单一;
@ParameterizedTest:表示为参数化测试
@DisplayName:为测试方法或者类设置展示名称
@BeforeEach:每个单元测试之前执行
@Diabled:表示该测试方法不执行
@Timeout:表示测试方法运行超过指定时间就会返回错误
@ExtendWith:为测试扩展引用
@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
DataSource dataSource;
@Autowired
UserMapper userMapper;
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Test
void contextLoads() {
// jdbcTemplate.queryForObject("select * from account_tbl")
// jdbcTemplate.queryForList("select * from account_tbl",)
Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);
log.info("记录总数:{}", aLong);
log.info("数据源类型:{}", dataSource.getClass());
}
断言:
简而言之就是对测试进行条件判断:检查业务逻辑返回的数据是否合理;测试结束后,会有测试报告;
方法 | 说明 |
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
package com.atguigu.admin;
import org.aspectj.lang.annotation.After;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
/**
* @author diao 2022/2/8
*/
/*
* @SpringBootTest中包含了@BootStrapWith与@ExtendWith:作用就是以spring的方式去驱动测试
* 所以每次开启时要启动容器
* */
@SpringBootTest
public class tt {
//在所有测试前执行
@BeforeAll
void testBeforeAll() {
System.out.println("我才是最先执行的");
}
/*测试前置条件:与断言assert大致相同,不过它失败了不会抛出异常
只会导致下面的测试不会进行
* */
@DisplayName("测试前置条件")
@Test
void testassumptions() {
Assumptions.assumeTrue(true, "结果不是true");
//只有前置条件为true,后面的才会响应
System.out.println("111111");
}
@DisplayName("测试前置条件2")
@Test
void testassumptions2() {
Assumptions.assumeFalse(true, "结果不是false");
// 因为前面前置条件不满足false,所以下面的不进行
System.out.println("111111");
}
/*嵌套测试:更加有层次感
*
* */
// 断言机制,前面断言失败那么后面的都不会执行
@DisplayName("测试断言机制")
@Test
void testSimpleAssertions() {
int cal = cal(3, 3);
//进行断言,判断
Object obj1 = new Object();
Object obj2 = new Object();
// assertSame(obj1,obj2);
}
int cal(int a, int b) {
int num = a + b;
return num;
}
@DisplayName("测试displayname注解")
@Test
void testDisplayName() {
System.out.println(1);
}
@DisplayName("测试2")
@Test
void testDisplayName2() {
System.out.println(2);
}
@Timeout(value = 500, unit = TimeUnit.SECONDS)//500ms则超时
@Test
void TestTimeOut() throws InterruptedException {
//因为超过500ms就会抛出异常,我们这里sleep400ms
Thread.sleep(400);
}
/*@RepeatedTest():重复测试
* */
@RepeatedTest(5)
@Test
void test3() {
System.out.println("重复测试中...");
}
// 在每一个单元测试之前执行
@BeforeEach
void testBeforeEach() {
System.out.println("测试马上开始");
}
// 在每一个单元测试之后执行
@AfterEach
void testAfterEach() {
System.out.println("测试结束了...");
}
// 在所有测试之后执行
@AfterAll
void TestAfterAll() {
System.out.println("我是最后一个执行的...");
}
}
前置条件(assumptions)
类似于断言,但是区别在于它判断失败不会因此抛出异常,assumptions只会让测试方法执行终止;
@DisplayName("前置条件")
public class AssumptionsTest {
private final String environment = "DEV";
@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV") //如果前置失败则不会打印这个
);
}
}
嵌套测试
主要是通过内部类和@Nested注解实现嵌套测试
@Nested//嵌套测试的注解
@DisplayName("when new")
class WhenNew{
@BeforeEach//在每一个单元测试之前创建一个新的栈
void createNewStack(){
stack=new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty(){
//进行断言,测试栈是不是为空,空就ok
assertTrue(stack.isEmpty());
}
/*assertThrows
*断言判断异常,如果用到了数据结构,对数据结构进行操作后再进行判断的话
* Stack::操作->assertThrows(异常.class,Stack::操作)
* */
@Test
@DisplayName("throws EmptyStackException when popped")
void throwExceptionOfPopped(){//抛出异常,如果stack pop后为空
assertThrows(EmptyStackException.class,stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeekded(){
assertThrows(EmptyStackException.class,stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing{
String element="an Element";
@BeforeEach
void pushElement(){
stack.push(element);
}
@Test
@DisplayName("judage empty")
void isNotEmpty(){
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementPopped(){
//断言判断stack pop()出来的元素是否和element相等
assertEquals(element,stack.pop());
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("ElementOfPeekIsEmpty")
void returnElementPeeked(){
assertEquals(element,stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
参数化测试:
好处:不需要每次加一个参数就新增一个单元测试,减少了代码冗余;
@ValueSource:八大基础类型+String+Class类型;
@NullSource:为参数化测试提供一个null的入参;
@EnumSource:为参数化入参提供一个枚举入参;
@MethodSource:方法返回值作为测试的入参参数(方法的返回需要是一个流);
@CsvFileSource:CSV内容作为参数...;
@ParameterizedTest
@DisplayName("单元测试")
@ValueSource(ints = {1,2,3,4,5}) //可以放八大类型
void testParameterizedTest(int i){
System.out.println(i);
}
@ParameterizedTest
@DisplayName("参数测试")
@MethodSource("stringProvider")
void testParameterized2(String i){
System.out.println(i);
}
static Stream<String> stringProvider() {
return Stream.of("apple", "banana", "atguigu");
}