Junit Test(从junit -> spring-test-> spring-boot-test)

Junit

使用方式

Junit4

  1. 引入依赖包
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>
  1. 编写测试类
 import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 
 public class CalculatorTest {
 
     @Test
     public void testAdd() {
         Calculator calculator = new Calculator();
         int result = calculator.add(2, 3);
         assertEquals(5, result);
     }
 }
  1. 常用注解
   @Test: 标记一个方法为测试方法。
   @Before: 每个测试方法之前运行。
   @After: 每个测试方法之后运行。
   @BeforeClass: 在所有测试方法之前运行,仅运行一次。
   @AfterClass: 在所有测试方法之后运行,仅运行一次。

Junit5

  1. 引入依赖包
   <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
       <version>5.10.2</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-engine</artifactId>
       <version>5.10.2</version>
       <scope>test</scope>
   </dependency>
   
   <!-- 或者只引入下面的就可以了-->
   <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter</artifactId>
       <version>5.10.2</version>
       <scope>test</scope>
   </dependency>
  1. 编写测试类
   import org.junit.jupiter.api.Test;
   import static org.junit.jupiter.api.Assertions.assertEquals;
   
   public class CalculatorTest {
   
       @Test
       public void testAdd() {
           Calculator calculator = new Calculator();
           int result = calculator.add(2, 3);
           assertEquals(5, result);
       }
   }
   
  1. 常用注解
   @Test: 标记一个方法为测试方法。
   @BeforeEach: 每个测试方法之前运行。
   @AfterEach: 每个测试方法之后运行。
   @BeforeAll: 在所有测试方法之前运行,仅运行一次。
   @AfterAll: 在所有测试方法之后运行,仅运行一次。

Junit 和Junit5 区别

架构和模块化
  • JUnit 4:
    • 单一 jar 文件。
    • 所有功能都集中在一个库中。
  • JUnit 5:
    • 模块化设计,由三个子项目组成:
      • JUnit Platform: 提供运行测试的基础设施。
      • JUnit Jupiter: 提供新的测试编写和扩展模型。
      • JUnit Vintage: 提供运行 JUnit 3 和 JUnit 4 测试的支持。
注解
  • JUnit 4:
    • @Test: 标记测试方法。
    • @Before: 每个测试方法之前运行。
    • @After: 每个测试方法之后运行。
    • @BeforeClass: 在所有测试方法之前运行,仅运行一次。
    • @AfterClass: 在所有测试方法之后运行,仅运行一次。
    • @Ignore: 忽略测试方法。
  • JUnit 5:
    • @Test: 标记测试方法。
    • @BeforeEach: 每个测试方法之前运行。
    • @AfterEach: 每个测试方法之后运行。
    • @BeforeAll: 在所有测试方法之前运行,仅运行一次。
    • @AfterAll: 在所有测试方法之后运行,仅运行一次。
    • @Disabled: 忽略测试方法或测试类。
    • @Nested: 用于表示嵌套的测试类。
    • @Tag: 用于标记测试以便进行分组和过滤。
    • @DisplayName: 为测试方法提供自定义名称。
断言
  • JUnit 4:
    • 使用 org.junit.Assert 类中的静态方法,例如 assertEqualsassertTrue 等。
  • JUnit 5:
    • 使用 org.junit.jupiter.api.Assertions 类中的静态方法,增加了一些新的断言方法和功能,例如 assertAllassertThrows 等。
条件测试执行
  • JUnit 4:
    • 通过第三方库(如 Assume)实现条件测试执行。
  • JUnit 5:
    • 内置条件执行注解,如 @EnabledOnOs@DisabledOnOs@EnabledOnJre@DisabledOnJre@EnabledIf@DisabledIf 等。
动态测试
  • JUnit 4:
    • 不支持动态测试。
  • JUnit 5:
    • 使用 @TestFactoryDynamicTest 提供对动态测试的支持,可以在运行时生成测试用例。
参数化测试
  • JUnit 4:
    • 使用 @RunWith(Parameterized.class) 和其他注解来实现参数化测试。
  • JUnit 5:
    • 使用 @ParameterizedTest 注解,并结合多个源注解(如 @ValueSource@MethodSource@CsvSource 等)来实现更灵活和强大的参数化测试。
扩展模型
  • JUnit 4:
    • 通过 @RunWithTestRule 扩展功能。
  • JUnit 5:
    • 提供了一个强大的扩展模型,通过实现 Extension 接口,可以更灵活地创建和使用扩展。
向后兼容性
  • JUnit 4:
    • 没有向后兼容性问题,因为是单一框架。
  • JUnit 5:
    • 提供了 JUnit Vintage 引擎,允许在 JUnit 5 平台上运行 JUnit 3 和 JUnit 4 的测试,以便逐步迁移到 JUnit 5。

依赖包说明

Junit maven 包

Junit jupiter dependency

JUnit Jupiter API:提供了编写测试所需的所有注解和类,如 @Test, @BeforeEach, @AfterEach 等。

JUnit Jupiter Engine:用于执行基于 JUnit Jupiter 的测试。这个引擎在添加 junit-jupiter 依赖时自动包含。

所以引入下面的就可以

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>

Spring Test

spring-test 是 Spring Framework 提供的一个模块,旨在简化 Spring 应用程序的测试。它为测试 Spring 应用程序中的组件和集成提供了支持,特别是在使用 Spring 的上下文和依赖注入时。spring-test 模块提供了一些重要的功能和工具,帮助开发人员更容易地编写和执行测试。

  1. 引入依赖包
<dependencies>
    <!-- Spring Context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.22</version>
    </dependency>
    <!-- Spring Test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.22</version>
        <scope>test</scope>
    </dependency>
    <!-- JUnit Jupiter -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.9.3</version>
        <scope>test</scope>
    </dependency>
    <!-- Mockito -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>4.11.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

  1. 编写测试代码
   import org.junit.jupiter.api.Test;
   import org.junit.jupiter.api.extension.ExtendWith;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.test.context.ContextConfiguration;
   import org.mockito.junit.jupiter.MockitoExtension;
   import org.springframework.test.context.junit.jupiter.SpringExtension;
   
   import static org.junit.jupiter.api.Assertions.*;
   
   // 扩展JUnit 5以支持Spring
   @ExtendWith(SpringExtension.class)
   // 使用mock对象
   @ExtendWith(MockitoExtension.class)
   // 指定Spring配置类或XML配置文件
   @ContextConfiguration(classes = AppConfig.class)
   public class MyServiceTest {
   
       @Autowired
       private MyService myService;
   
       @Test
       public void testService() {
           String result = myService.performAction();
           assertEquals("expectedResult", result);
       }
   }
   

或者

   import org.junit.jupiter.api.Test;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
   
   import static org.junit.jupiter.api.Assertions.*;
   
   @SpringJUnitConfig(AppConfig.class)
   public class MyServiceTest {
   
       @Autowired
       private MyService myService;
   
       @Test
       public void testService() {
           String result = myService.performAction();
           assertEquals("expectedResult", result);
       }
   }

或者 Test SpringMVC

    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(classes = MyControllerTest.WebConfig.class)
    public class MyControllerTest {
    
        @Autowired
        private WebApplicationContext wac;
    
        private MockMvc mockMvc;
    
        @Configuration
        @EnableWebMvc
        static class WebConfig {
    
            @Bean
            public MyController myController() {
                return new MyController();
            }
        }
    
        @BeforeEach
        public void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
        }
    
        @Test
        public void testGetEndpoint() throws Exception {
            mockMvc.perform(get("/my-endpoint"))
                   .andExpect(status().isOk())
                   .andExpect(content().string("expectedResponse"));
        }
    }
    

@SpringJUnitConfig@ExtendWith(SpringExtension.class) 都是用于在 JUnit 5 中整合 Spring Test 框架的注解。它们有着相似的作用,但使用方式略有不同。

@ExtendWith(SpringExtension.class)

@ExtendWith(SpringExtension.class) 是 JUnit 5 提供的一种扩展机制,用于扩展测试的行为。SpringExtension 是 Spring Test 提供的一个扩展,用于在 JUnit 5 测试中启用 Spring Test 上下文。

使用 @ExtendWith(SpringExtension.class) 时,您通常需要另外添加 @ContextConfiguration 或其他 Spring 的配置注解来指定配置类或配置文件。

@SpringJUnitConfig

@SpringJUnitConfig 是一个复合注解,它结合了 @ExtendWith(SpringExtension.class)@ContextConfiguration,使得配置更加简洁。这意味着使用 @SpringJUnitConfig 不需要额外添加 @ContextConfiguration 注解,因为它已经包含了这一功能。

如果您只是进行基本的 Spring 测试,不需要其他额外的扩展,使用 @SpringJUnitConfig 会更加简洁和方便。

如果您的测试类中需要使用多个 JUnit 5 扩展(如 Mockito 等),使用 @ExtendWith(SpringExtension.class) 会更加灵活。

@ContextConfiguration 注解用于在 Spring 测试中指定 Spring 应用上下文的配置。通过这个注解,您可以告诉 Spring 在测试运行时加载哪些配置类或配置文件,从而初始化 Spring 应用上下文。

主要用途

  • 指定配置类:通过 classes 属性,您可以指定一个或多个 Java 配置类。
  • 指定配置文件:通过 locations 属性,您可以指定一个或多个 XML 配置文件的位置。
  • 继承上下文配置:在继承测试类时,可以通过 @ContextConfiguration 实现配置的继承和覆盖。

属性

  • classes:用于指定一个或多个配置类。
  • locations:用于指定一个或多个 XML 配置文件的路径。
  • initializers:用于指定一个或多个 ApplicationContextInitializer 实现类,可以在应用上下文刷新之前对其进行初始化。
  • inheritLocations:指定是否从超类继承 @ContextConfigurationlocations 属性,默认为 true
  • inheritInitializers:指定是否从超类继承 @ContextConfigurationinitializers 属性,默认为 true
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// 基类测试
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = BaseConfig.class)
public class BaseTest {

    @Autowired
    protected MyBaseService myBaseService;
}

// 子类测试,继承上下文配置
@ContextConfiguration(classes = {BaseConfig.class, ChildConfig.class}, inheritLocations = true)
public class ChildTest extends BaseTest {

    @Autowired
    private MyChildService myChildService;

    @Test
    public void testChildService() {
        assertNotNull(myBaseService);
        assertNotNull(myChildService);
    }
}

Spring-boot Test

  1. 引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>3.3.0</version>
    <scope>test</scope>
</dependency>

这个依赖项会自动引入必要的库来支持 Spring Boot 测试,包括 JUnit 5


image
  1. 编写测试类
   import org.junit.jupiter.api.Test;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.boot.test.context.SpringBootTest;
   import static org.assertj.core.api.Assertions.assertThat;
   
    // @RunWith(SpringRunner.class)是Junit4提供的注解,将Spring和Junit链接了起来。
    //假如使用Junit5,不再需要使用@RunWith注解,
    //@SpringBootTest和其它@*Test默认已经包含了该注解。
   @SpringBootTest
   public class MyServiceIntegrationTest {
   
       @Autowired
       private MyService myService;
   
       @Test
       public void testPerformAction() {
           String result = myService.performAction();
           assertThat(result).isEqualTo("expectedResult");
       }
   }

或者,mvcTest

   import org.junit.jupiter.api.Test;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
   import org.springframework.boot.test.mock.mockito.MockBean;
   import org.springframework.test.web.servlet.MockMvc;
   import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
   import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
   import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
   import static org.mockito.BDDMockito.given;
   
   @WebMvcTest(MyController.class)
   public class MyControllerTest {
   
       @Autowired
       private MockMvc mockMvc;
   
       @MockBean
       private MyService myService;
   
       @Test
       public void testGetEndpoint() throws Exception {
           given(myService.performAction()).willReturn("expectedResult");
   
           mockMvc.perform(get("/my-endpoint"))
                   .andExpect(status().isOk())
                   .andExpect(content().string("expectedResult"));
       }
   }
   

或者jpa test

   import org.junit.jupiter.api.Test;
   import org.springframework.beans.factory.annotation.Autowired;
   import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
   import static org.assertj.core.api.Assertions.assertThat;
   
   @DataJpaTest
   public class MyRepositoryTest {
   
       @Autowired
       private MyRepository myRepository;
   
       @Test
       public void testFindById() {
           MyEntity entity = new MyEntity();
           entity.setName("TestName");
           myRepository.save(entity);
   
           MyEntity found = myRepository.findById(entity.getId()).orElse(null);
           assertThat(found).isNotNull();
           assertThat(found.getName()).isEqualTo("TestName");
       }
   }
   

@SpringBootTest:用于加载整个 Spring 应用上下文进行集成测试。适用于需要测试多个组件协作的场景。

@WebMvcTest:用于加载 Web 层组件(如控制器)进行测试,不加载整个应用上下文。适用于测试控制器及其相关的过滤器、拦截器等。

@DataJpaTest:用于测试 JPA 持久层组件。它会自动配置嵌入式数据库(如 H2),并对持久层进行测试。

使用@SpringBootTest时并没有像@ContextConfiguration一样显示指定locations或classes属性,原因在于@SpringBootTest注解会自动检索程序的配置文件,检索顺序是从当前包开始,逐级向上查找被@SpringBootApplication或@SpringBootConfiguration注解的类。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容