JUnit5单元测试

JUnit5单元测试

本文章的编程环境为:使用Gradle构建的SpringBoot工程

参考资料:

  • https://junit.org/junit4/
  • https://junit.org/junit5/
  • https://www.bilibili.com/video/BV1u4411T78k?p=2

概述

简介

JUnit 是一个开源的 Java 语言的单元测试框架

  • 专门针对 Java 语言设计,使用最广泛
  • JUnit 是事实上的标准单元测试框架

测试驱动开发(TDD)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfO5dppi-1598015815152)(C:/Users/86189/AppData/Roaming/Typora/typora-user-images/image-20200821151848039.png)]

单元测试的优点

  • 确保单个方法运行正常
  • 如果修改了方法代码,只需确保其对应的单元测试通过
  • 测试代码本身就可以作为示例代码
  • 可以自动化运行所有测试并获得报告

单元测试的特点

  • 使用断言(Assertion)测试期望的结果
  • 可以方便地组织和运行测试
  • 可以方便地查看测试结果
  • 常用 IDE 都继承了 JUnit
  • 可以方便地集成到 Maven

JUnit的设计

  • TestCase:一个 TestCase 表示一个测试
  • TestSuite:一个 TestSuite 包含一组 TestCase,表示一组测试
  • TestFixture:一个 TestFixture 表示一个测试环境
  • TestResult:用于手机测试结果
  • TestRunner:用于运行测试
  • TestListener:用于监听测试过程,收集测试数据
  • Assert:用于断言测试结果是否正确

JUnit 在 Spring 中的不足

1)导致多次Spring容器初始化问题

根据JUnit测试方法的调用流程,每执行一个测试方法都会创建一个测试用例的实例并调用setUp()方法。由于一般情况下,我们在setUp()方法中初始化Spring容器,这意味着如果测试用例有多少个测试方法,Spring容器就会被重复初始化多次。虽然初始化Spring容器的速度并不会太慢,但由于可能会在Spring容器初始化时执行加载Hibernate映射文件等耗时的操作,如果每执行一个测试方法都必须重复初始化Spring容器,则对测试性能的影响是不容忽视的;

使用Spring测试套件,Spring容器只会初始化一次

2)需要使用硬编码方式手工获取Bean

在测试用例类中我们需要通过ctx.getBean()方法从Spirng容器中获取需要测试的目标Bean,并且还要进行强制类型转换的造型操作。这种乏味的操作迷漫在测试用例的代码中,让人觉得烦琐不堪;

使用Spring测试套件,测试用例类中的属性会被自动填充Spring容器的对应Bean,无须在手工设置Bean!

3)数据库现场容易遭受破坏

测试方法对数据库的更改操作会持久化到数据库中。虽然是针对开发数据库进行操作,但如果数据操作的影响是持久的,可能会影响到后面的测试行为。举个例子,用户在测试方法中插入一条ID为1的User记录,第一次运行不会有问题,第二次运行时,就会因为主键冲突而导致测试用例失败。所以应该既能够完成功能逻辑检查,又能够在测试完成后恢复现场,不会留下“后遗症”;

使用Spring测试套件,Spring会在你验证后,自动回滚对数据库的操作,保证数据库的现场不被破坏,因此重复测试不会发生问题!

4)不方便对数据操作正确性进行检查

假如我们向登录日志表插入了一条成功登录日志,可是我们却没有对t_login_log表中是否确实添加了一条记录进行检查。一般情况下,我们可能是打开数据库,肉眼观察是否插入了相应的记录,但这严重违背了自动测试的原则。试想在测试包括成千上万个数据操作行为的程序时,如何用肉眼进行检查?

只要你继承Spring的测试套件的用例类,你就可以通过jdbcTemplate(或Dao等)在同一事务中访问数据库,查询数据的变化,验证操作的正确性!

Annotations

@BeforeEach & @AfterEach

@BeforeEach: Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited unless they are overridden.

@AfterEach: Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @After. Such methods are inherited unless they are overridden.

  • 在 @BeforeEach 方法中初始化测试资源
  • 在 @AfterEach 方法中释放测试资源

@BeforeAll & @AfterAll

@BeforeAll: Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the “per-class” test instance lifecycle is used).

@AfterAll: Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @AfterClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the “per-class” test instance lifecycle is used).

  • @BeforeAll 静态方法初始化的对象只能存放在静态字段中
  • 静态字段的状态会影响到所有的 @Test

超时 —— @Timeout

@Test
@Timeout(value = 1, unit = TimeUnit.MICROSECONDS)
void test07() {
   List<Integer> list = new ArrayList<>();
   for (int i = 0; i < 100000; i++) {
      list.add(i);
   }
}

禁用 —— @Disabled

Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited.

重复测试 —— @RepeatedTest

Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden.

重复测试:使用参数 value 来指定重复测试的次数

参数化测试 —— @ParameterizedTest

Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden.

参数源详解

value source:最简单的参数源,通过注解可以直接指定携带的运行参数。

  • String values: @ValueSource(strings = {“foo”, “bar”, “baz”})
  • Double values: @ValueSource(doubles = {1.5D, 2.2D, 3.0D})
  • Long values: @ValueSource(longs = {2L, 4L, 8L})
  • Integer values: @ValueSource(ints = {2, 4, 8})
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void test03(int n) {
   Assertions.assertTrue(n > 0);
}

Enum Source:枚举参数源,允许我们通过将参数值由给定Enum枚举类型传入。并可以通过制定约束条件或正则匹配来筛选传入参数。

@ParameterizedTest
@EnumSource(value = DayInWeekEnum.class, names = {"MONDAY"})
void test04(DayInWeekEnum day) {
   Assertions.assertTrue(EnumSet.of(DayInWeekEnum.MONDAY, DayInWeekEnum.FRIDAY).contains(day));
}

Method Source:通过其他的 Java 方法函数来作为参数源。引用的方法返回值必须是Stream、Iterator 或者 Iterable。

static Stream<String> stringStreamGenerator() {
   return Stream.of("a", "b", "c", "d");
}

@ParameterizedTest
@MethodSource(value = {"stringStreamGenerator"})
void test05(String arg) {
   System.out.println(arg);
   Assertions.assertNotNull(arg);
}

Argument Source:通过参数类来作为参数源。这里引用的类必须实现ArgumentsProvider接口。

CSV Source:通过指定 csv(comma-separated-values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号)格式的注解作为参数源。

static Map<Integer, String> map;

@BeforeAll
static void mapAdding() {
   map = new HashMap<>();
   map.put(1, "Tom");
   map.put(2, "Lucy");
   map.put(3, "Jack");
}

@ParameterizedTest
@CsvSource({"1,Tom", "2,Lucy", "3,Jack"})
void test06(Integer id, String name) {
   System.out.println(id + "  ->  " + name);
   Assertions.assertTrue(map.containsKey(id));
   Assertions.assertEquals(name, map.get(id));
}

CSV File Source:除了使用csv参数源,这里也支持使用csv文件作为参数源。

假设users.csv 文件包含如下csv格式的数据:1,Selma ‘\n’ 2,Lisa’\n’ 3,Tim(’\n’ :换行)

@ParameterizedTest
@CsvFileSource(resources = "/users.csv")
void testUsersFromCsv(long id, String name) {
    assertTrue(idToUsername.containsKey(id));
    assertTrue(idToUsername.get(id).equals(name));
}
参数转换

JUnit allows us to convert arguments to the target format we need in our tests.

隐式转换

JUnit 提供了很对内建的格式转化支持,特别是 string 和常用的数据类型。

以下是支持和 string 型进行转换的类型:

  • Boolean
  • Byte
  • Character
  • Short
  • Integer
  • Long
  • Float
  • Double
  • Enum subclass
  • Instant
  • LocalDate
  • LocalDateTime
  • LocalTime
  • OffsetTime
  • OffsetDateTime
  • Year
  • YearMonth
  • ZonedDateTime

显式转换

Junit5 中可以使用 @ConvertWith(MyConverter.class) 注解来实现 SimpleArgumentConverter。

标记与过滤 —— @Tag

可以在测试类上标记 @Tag("…") ,之后在 pom.xml 文件或 build.gradle 文件中可以对其进行过滤。

Assertions

Assertions’ basic methods

  • 断言相等:assertEquals(100, x)
  • 断言数组相等:assertArrayEquals({1, 2, 3}, x)
  • 浮点数断言相等:assertEquals(3.1415, x, 0.0001)
  • 断言为 null:assertNull(x)
  • 断言为 true/false:assertTrue(x > 0) 、assertFalse(x < 0)
  • 其他:assertNotEquals / assertNotNull

断言组 —— assertAll

此方法可实现1个用例中包含多个断言,遇到断言失败仍然会继续下一个断言。

assertAll 的 heading 将会在测试失败时,会在失败信息的开头,作为一系列失败点的开头信息,给予提示。

@ParameterizedTest
@CsvSource({"1,Tom", "2,Lucy", "3,Jack"})
void test06(Integer id, String name) {
   System.out.println(id + "  ->  " + name);
   // Assertions.assertTrue(map.containsKey(id));
   // Assertions.assertEquals(name, map.get(id));
   Assertions.assertAll("This is the heading of method: assertAll",
         () -> Assertions.assertTrue(map.containsKey(id)),
         () -> Assertions.assertEquals(name, map.get(id)),
         () -> Assertions.assertEquals("1", map.get(id))
   );
}

超时 —— assertTimeout

@Test
void test08() {
   Assertions.assertTimeout(Duration.ofMillis(1), () -> {
      List<Integer> list = new ArrayList<>();
      for (int i = 0; i < 1000000; i++) {
         list.add(i);
      }
   });
}

异常测试 —— assertThrows

使用 java8 中的 lambda 表达式配合 assertThrows 进行测试。

@Test
void test02() {
   Exception exception = Assertions.assertThrows(ArithmeticException.class, () -> calculator.divide(2, 0));
   Assertions.assertEquals("/ by zero", exception.getMessage());
}

JUnit的使用

Junit测试的执行顺序

  1. 实例化测试类
  2. 执行 @BeforeAll 方法:初始化非常耗时的资源,例如:创建数据库(针对所有测试,只执行一次,且必须为static void)
  3. 执行 @BeforeEach 方法:初始化测试对象,例如:I/O流
  4. 执行 @Test 方法:测试方法,在这里可以测试期望异常和超时时间
  5. 执行 @AfterEach 方法:释放资源,销毁 @Before 创建的测试对象
  6. 执行 @AfterAll 方法:清理 @BeforeAll 创建的资源,例如:删除数据库(针对所有测试,只执行一次,且必须为static void)

JUnit4 与 JUnit5 的不同

  • 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
  • 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
  • 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
  • 把@Ignore 替换成@Disabled。
  • 把@Category 替换成@Tag。
  • 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。

配合 Spring Boot 使用 JUnit5

  • 在测试类上加 @SpringBootTest 注解
  • 可以使用 @Autowired 注解自动注入要测试的服务等

Mock 模拟

@MockBean & @SpyBean
  • @MockBean
  • @SpyBean
@SpringBootTest
class JunitDemoApplicationTests {

   // static Calculator calculator;
   //
   // @BeforeAll
   // static void createCalculator() {
   //     calculator = new Calculator();
   // }

   @MockBean
   Calculator1 calculator1;
    
   @SpyBean
   Calculator2 calculator2;

   @Test
   void test00() {
       when(calculator1.add(1, 2)).thenReturn(3);
      Assertions.assertEquals(3, calculator1.add(1, 2));
   }
    
   @Test
   void test01() {
      Assertions.assertEquals(3, calculator2.add(1, 2));
   }

}
MockMvc —— 模拟 HTTP 请求

MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。

接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。

  1. mockMvc.perform执行一个请求。
  2. MockMvcRequestBuilders.get(“XXX”)构造一个请求。
  3. ResultActions.param添加请求传值
  4. ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
  5. ResultActions.andExpect添加执行完成后的断言。
  6. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
  7. ResultActions.andReturn表示执行完成后返回相应的结果。
常用的测试
  1. 测试普通控制器
mockMvc.perform(get("/user/{id}", 1)) //执行请求  
            .andExpect(model().attributeExists("user")) //验证存储模型数据  
            .andExpect(view().name("user/view")) //验证viewName  
            .andExpect(forwardedUrl("/WEB-INF/jsp/user/view.jsp"))//验证视图渲染时forward到的jsp  
            .andExpect(status().isOk())//验证状态码  
            .andDo(print()); //输出MvcResult到控制台

1234567
  1. 得到MvcResult自定义验证
MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求  
        .andReturn(); //返回MvcResult  
Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言 

1234
  1. 验证请求参数绑定到模型数据及Flash属性
mockMvc.perform(post("/user").param("name", "zhang")) //执行传递参数的POST请求(也可以post("/user?name=zhang"))  
            .andExpect(handler().handlerType(UserController.class)) //验证执行的控制器类型  
            .andExpect(handler().methodName("create")) //验证执行的控制器方法名  
            .andExpect(model().hasNoErrors()) //验证页面没有错误  
            .andExpect(flash().attributeExists("success")) //验证存在flash属性  
            .andExpect(view().name("redirect:/user")); //验证视图  
123456
  1. 文件上传
byte[] bytes = new byte[] {1, 2};  
mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传  
        .andExpect(model().attribute("icon", bytes)) //验证属性相等性  
        .andExpect(view().name("success")); //验证视图 
1234
  1. JSON请求/响应验证
String requestBody = "{\"id\":1, \"name\":\"zhang\"}";  
    mockMvc.perform(post("/user")  
            .contentType(MediaType.APPLICATION_JSON).content(requestBody)  
            .accept(MediaType.APPLICATION_JSON)) //执行请求  
            .andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType  
            .andExpect(jsonPath("$.id").value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/  
      
    String errorBody = "{id:1, name:zhang}";  
    MvcResult result = mockMvc.perform(post("/user")  
            .contentType(MediaType.APPLICATION_JSON).content(errorBody)  
            .accept(MediaType.APPLICATION_JSON)) //执行请求  
            .andExpect(status().isBadRequest()) //400错误请求  
            .andReturn();  
      
    Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体

12345678910111213141516
  1. 异步测试
//Callable  
    MvcResult result = mockMvc.perform(get("/user/async1?id=1&name=zhang")) //执行请求  
            .andExpect(request().asyncStarted())  
            .andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时  
            .andReturn();  
      
    mockMvc.perform(asyncDispatch(result))  
            .andExpect(status().isOk())  
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))  
            .andExpect(jsonPath("$.id").value(1));  
12345678910
  1. 全局配置
mockMvc = webAppContextSetup(wac)  
            .defaultRequest(get("/user/1").requestAttr("default", true)) //默认请求 如果其是Mergeable类型的,会自动合并的哦mockMvc.perform中的RequestBuilder  
            .alwaysDo(print())  //默认每次执行请求后都做的动作  
            .alwaysExpect(request().attribute("default", true)) //默认每次执行后进行验证的断言  
            .build();  
      
    mockMvc.perform(get("/user/1"))  
            .andExpect(model().attributeExists("user"));
简单的例子

创建 VO 对象:

@Data
public class TestVO {
    private Integer id;
    private String name;
}

创建控制器:

@RestController
public class MyController {
    @GetMapping(value = {"/test"})
    public Object test() {
        TestVO testVO = new TestVO();
        testVO.setId(1);
        testVO.setName("Test1");
        return testVO;
    }
}

创建测试类:

@SpringBootTest
class MyControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Test
    void test() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        // 发送 get 请求
        // public static org.springframework.test.web.servlet.request
        //      .MockHttpServletRequestBuilder get(@NotNull String urlTemplate, Object... uriVars)
        RequestBuilder request = get("http://localhost:8080/test");
        try {
            String response = mockMvc.perform(request).andReturn().getResponse().getContentAsString();
            System.out.println(response);
            // jackson 解析
            ObjectMapper mapper = new ObjectMapper();
            TestVO testVO = mapper.readValue(response, TestVO.class);
            System.out.println(testVO);
            Assertions.assertEquals(1, testVO.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

事务控制

加 @Rollback 注解即可实现在测试方法完成后的回滚

注:笔者上网查了许久,发现都是通过 @Transactional 与 @Rollback 配合使用的,但笔者在 2020/8/21 使用 Spring Boot 进行单元测试的时候,发现其已无 @Transactional 注解,而且只使用 @Rollback 注解就可以完成回滚操作。

为了测试单独使用 @Rollback 是否可以达成事务回滚的效果,笔者写了以下的测试类:

@SpringBootTest
class CalculatorServiceTest { // 两个标有 @Test 的测试方法,会依照方法名开头字母(的ASCII码值)升序进行
    private int a = 0; // a 的默认值为 0

    @Test
    @Rollback // 回滚注解
    void test0() {
        a = 1; // 将 a 的值修改为 1
        System.out.println("a = " + a); // 在标记了 @Rollback 回滚注解的方法结束前,输出 a 的值,
    }

    @Test
    void test1() {
        System.out.println("a = " + a);
    }

}

输出内容如下:

...(日志信息)
a = 1 // test0() 的输出结果
a = 0 // test1() 的输出结果
BUILD SUCCESSFUL in 2s
4 actionable tasks: 2 executed, 2 up-to-date
19:22:25: Task execution finished ':test --tests "xxx.yyyy.service.CalculatorServiceTest"'.

发现 a 在 test0() 方法中确实被修改为了 1,而且在 test0() 方法结束后确实也被修改回去了。姑且推测其是 “回滚成功” 了。

再看向 @Rollback 的源码(省略了开头的 Copyright):

package org.springframework.test.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * {@code @Rollback} is a test annotation that is used to indicate whether
 * a <em>test-managed transaction</em> should be <em>rolled back</em> after
 * the test method has completed.
 *
 * <p>Consult the class-level Javadoc for
 * {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
 * for an explanation of <em>test-managed transactions</em>.
 *
 * <p>When declared as a class-level annotation, {@code @Rollback} defines
 * the default rollback semantics for all test methods within the test class
 * hierarchy. When declared as a method-level annotation, {@code @Rollback}
 * defines rollback semantics for the specific test method, potentially
 * overriding class-level default commit or rollback semantics.
 *
 * <p>As of Spring Framework 4.2, {@code @Commit} can be used as direct
 * replacement for {@code @Rollback(false)}.
 *
 * <p><strong>Warning</strong>: Declaring {@code @Commit} and {@code @Rollback}
 * on the same test method or on the same test class is unsupported and may
 * lead to unpredictable results.
 *
 * <p>This annotation may be used as a <em>meta-annotation</em> to create
 * custom <em>composed annotations</em>. Consult the source code for
 * {@link Commit @Commit} for a concrete example.
 *
 * @author Sam Brannen
 * @since 2.5
 * @see Commit
 * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Rollback {

   /**
    * Whether the <em>test-managed transaction</em> should be rolled back
    * after the test method has completed.
    * <p>If {@code true}, the transaction will be rolled back; otherwise,
    * the transaction will be committed.
    * <p>Defaults to {@code true}.
    */
   boolean value() default true;

}

其中有两段话较为关键:

@Rollback is a test annotation that is used to indicate whether a test-managed transaction should be rolled back after the test method has completed.
Whether the test-managed transaction should be rolled back after the test method has completed.

If true, the transaction will be rolled back; otherwise, the transaction will be committed.

Defaults to true.

第一段话是注释在整个 @Rollback 注解类上的,大意为:@Rollback 用于在测试完成之后,回滚其所标记的类/方法。

第二段话是注释的 value() 上的,该属性表示 “是否开启回滚” ,默认值为 true(即回滚,即不提交)。

结论:只使用 @Rollback 是可以完成测试回滚功能的。

总结

  • 一个 TestCase 包含一组相关的测试方法
  • 使用 Assert 断言测试结果(注意浮点数 assertEquals 要指定 delta)
  • 每个测试方法必须完全独立
  • 测试代码必须非常简单
  • 不能为测试代码再编写测试
  • 测试需要覆盖各种输入条件,特别是边界条件
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JUnit 5 是 Java 编程语言的单元测试框架,它是 JUnit 团队开发的最新版本。JUnit 5 提供了一套强大的工具和功能,用于编写和执行单元测试。下面是 JUnit 5 单元测试的一些重要特性和用法: 1. 注解驱动:JUnit 5 使用注解来标记测试方法和测试类。常用的注解包括 `@Test`、`@BeforeEach`、`@AfterEach` 等。 2. 断言方法:JUnit 5 提供了丰富的断言方法,用于验证测试结果是否符合预期。例如,`assertEquals()`、`assertTrue()`、`assertNotNull()` 等。 3. 参数化测试:JUnit 5 支持参数化测试,可以通过 `@ParameterizedTest` 注解来定义参数化测试方法,并使用 `@ValueSource`、`@CsvSource` 等注解提供测试参数。 4. 嵌套测试:JUnit 5 允许在一个测试类中嵌套其他测试类,以更好地组织和管理测试代码。 5. 扩展模型:JUnit 5 引入了扩展模型,通过实现扩展接口可以自定义测试生命周期、测试报告、参数解析等行为。 6. 并发执行:JUnit 5 支持并发执行测试,可以通过 `@Execution` 注解来配置并发策略。 7. 动态测试:JUnit 5 允许在运行时动态生成测试用例,通过 `DynamicTest` 接口和 `@TestFactory` 注解实现。 8. 条件测试:JUnit 5 提供了条件测试的功能,可以根据条件来决定是否执行某个测试方法。 以上是 JUnit 5 单元测试的一些重要特性和用法。如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值