<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      SpringBoot集成測試筆記:縮小測試范圍、提高測試效率

      背景

      在 SpringBoot 中,除了基于 Mock 的單元測試,往往還需要執行幾個模塊組合的集成測試。一種簡單的方法就是在測試類上加入 @SpringBootTest 注解,但是,如果不對該注解做一些配置,默認情況下該測試類會加載完整的 SpringBoot 環境,包括該程序中所有的 Bean。如果要初始化的 Bean 非常多,啟動集成測試的時間就會很長,因此我們需要對 @SpringBootTest 注解進行一些配置,以減少環境加載的數量,提高程序運行效率。

      項目架構

      下面是一個簡單的 SpringBoot 項目,類圖如下:

      類圖

      • ProjectController 依賴接口 ProjectListServiceProjectOperateService
      • ProjectListService 的實現類依賴接口 ProjectConverterProjectMapper
      • ProjectOperateService 的實現類依賴接口 ProjectBizCheckServiceTechCheckServiceProjectConververProjectMapper;
      • 接口 ProjectConverterMapStruct映射接口;
      • 接口 ProjectMapperMybatis 數據訪問接口(DAO)。

      不帶參數的 @SpringBootTest 測試類

      從類圖中可以看到,ProjectListService 的實現類依賴兩個接口,分別是用于對象轉換的 ProjectConverter 和數據訪問接口 ProjectMapper。我們首先使用默認的配置,即不帶參數的 @SpringBootTest 注解進行測試。測試類代碼如下:

      /**
       * 直接采用 {@link SpringBootTest} 注解的集成測試,
       * 不帶任何參數或配置
       *
       */
      @SpringBootTest
      @DisplayName("集成測試:不帶任何參數或配置")
      class ProjectListServiceWithoutConfigsTest {
          private final ApplicationContext applicationContext;
      
          @Autowired
          private ProjectListService projectListService;
      
          public ProjectListServiceWithoutConfigsTest(ApplicationContext applicationContext) {
              this.applicationContext = applicationContext;
          }
      
          @Test
          @DisplayName("獲取所有Bean名稱和數量")
          public void printAllBean() {
              // 獲取所有Bean名稱
              String[] beanNames = applicationContext.getBeanDefinitionNames();
              Arrays.sort(beanNames);
              System.out.println("========== Spring Beans Total (" + beanNames.length + ") =========");
              for (String beanName : beanNames) {
                  System.out.println("name=" + beanName + ", class=" + applicationContext.getBean(beanName).getClass());
              }
          }
      
          @Test
          @DisplayName("測試查詢全部項目列表-應包含2個項目,且和數據庫一致")
          void testListAllProjects() {
              List<ProjectListResponse> projectListResponses = projectListService.listProjects();
      
              assertThat(projectListResponses).hasSize(2);
              assertThat(projectListResponses.getFirst().getProjectId()).isEqualTo(1);
              assertThat(projectListResponses.getFirst().getProjectName()).isEqualTo("測試項目1");
              assertThat(projectListResponses.getFirst().getProjectStatus()).isEqualTo(ProjectStatus.READY.getDesc());
      
              assertThat(projectListResponses.get(1).getProjectId()).isEqualTo(2);
              assertThat(projectListResponses.get(1).getProjectName()).isEqualTo("測試項目2");
              assertThat(projectListResponses.get(1).getProjectStatus()).isEqualTo(ProjectStatus.RUNNING.getDesc());
          }
      }
      

      這里我們實現了一個方法 printAllBean(),通過獲取應用上下文 ApplicationContext 對象中的所有被 Spring 加載的 Bean,檢查本次測試加載的 Bean 數量。

      運行測試,printAllBean() 方法的輸出如下:

      ========== Spring Beans Total (289) =========
      name=/project, class=class cn.asuka.itd.project.controller.ProjectController
      name=accessorsProvider, class=class springfox.documentation.schema.property.bean.AccessorsProvider
      name=apiDescriptionLookup, class=class springfox.documentation.spring.web.scanners.ApiDescriptionLookup
      name=apiDescriptionReader, class=class springfox.documentation.spring.web.scanners.ApiDescriptionReader
      name=apiDocumentationScanner, class=class springfox.documentation.spring.web.scanners.ApiDocumentationScanner
      ......
      name=welcomePageNotAcceptableHandlerMapping, class=class org.springframework.boot.autoconfigure.web.servlet.WelcomePageNotAcceptableHandlerMapping
      name=xmlModelPlugin, class=class springfox.documentation.schema.plugins.XmlModelPlugin
      name=xmlPropertyPlugin, class=class springfox.documentation.schema.property.XmlPropertyPlugin
      

      可以看到總共加載了 289 個 Bean,數量很多,但大多數是我們在測試中不直接依賴的。

      那么,我們應該如何讓該測試只依賴我們需要的 Bean,或者盡可能減少依賴的 Bean 數量呢?

      帶參數的 @SpringBootTest 測試

      首先,我們要知道的是基于 MapStructProjectConverter 接口,在編譯期會生成對應的實現類 ProjectConverterImpl,和 ProjectConverter 在同一個包下,因此我們實際上可以直接把該實現類加載進 Spring 上下文中。

      但是,基于 MybatisProjectMapper 并不會直接生成實現類,而是在運行期通過 MapperProxy 代理類去執行。此外我們使用的數據庫連接池是 Druid,因此 ProjectMapper 也隱含了對 Druid 連接池的依賴。

      因此,我們通過設置 @SpringBootTest 注解的 classes 參數,來指定本次測試中 Spring 上下文需要加載的類。

      為了讓測試代碼能夠調用 Druid 連接池,還需要建立一個 MybatisTestConfig 的配置類,人為地設置一個在測試環境下的 DataSource 對象,讓我們的測試類依賴該數據源,而不是生產代碼中的數據源。

      MybatisTestConfig 配置類定義如下:

      /**
       * @author jwmao
       */
      @TestConfiguration
      @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisAutoConfiguration.class})
      @MapperScan(basePackages = {"cn.asuka.itd.project.dao"})
      public class MybatisTestConfig {
          @Value("${spring.datasource.druid.url}")
          private String url;
      
          @Value("${spring.datasource.druid.username}")
          private String username;
      
          @Value("${spring.datasource.druid.password}")
          private String password;
      
          @Value("${spring.datasource.druid.driver-class-name}")
          private String driverClassName;
      
          @Bean
          @ConfigurationProperties(prefix = "spring.datasource")
          public DataSource dataSource() {
              DruidDataSource dataSource = new DruidDataSource();
              dataSource.setUrl(url);
              dataSource.setUsername(username);
              dataSource.setPassword(password);
              dataSource.setDriverClassName(driverClassName);
              return dataSource;
          }
      
          @Bean
          public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
              SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
              sessionFactory.setDataSource(dataSource);
              sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                      .getResources("classpath:mapper/*.xml"));
              return sessionFactory.getObject();
          }
      }
      

      除了指定數據源 DataSource 對象,還需要指定 SqlSessionFactory 對象,因為 MapperProxy 類依賴它,如果不指定的話它不會自動注入。

      下面來看一下 ProjectListServiceWithConfigsTest 的實現:

      /**
       * 指定測試依賴Bean的測試類
       */
      @SpringBootTest(classes = {
              ProjectListServiceImpl.class,
              MybatisTestConfig.class,
              ProjectConverterImpl.class
      })
      @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
      @PropertySource("classpath:application.properties")
      @DisplayName("集成測試:指定測試依賴Bean")
      class ProjectListServiceWithConfigsTest {
          private final ApplicationContext applicationContext;
      
          @Autowired
          private ProjectListServiceImpl projectListServiceUnderTest;
      
          public ProjectListServiceWithConfigsTest(ApplicationContext applicationContext) {
              this.applicationContext = applicationContext;
          }
      
          @Test
          @DisplayName("獲取所有Bean名稱和數量")
          public void printAllBean() {
              // 獲取所有Bean名稱
              String[] beanNames = applicationContext.getBeanDefinitionNames();
              Arrays.sort(beanNames);
              System.out.println("========== Spring Beans Total (" + beanNames.length + ") =========");
              for (String beanName : beanNames) {
                  System.out.println("name=" + beanName + ", class=" + applicationContext.getBean(beanName).getClass());
              }
          }
      
          @Test
          @DisplayName("測試查詢全部項目列表-應包含2個項目,且和數據庫一致")
          void testListAllProjects() {
              List<ProjectListResponse> projectListResponses = projectListServiceUnderTest.listProjects();
      
              assertThat(projectListResponses).hasSize(2);
              assertThat(projectListResponses.getFirst().getProjectId()).isEqualTo(1);
              assertThat(projectListResponses.getFirst().getProjectName()).isEqualTo("測試項目1");
              assertThat(projectListResponses.getFirst().getProjectStatus()).isEqualTo(ProjectStatus.READY.getDesc());
      
              assertThat(projectListResponses.get(1).getProjectId()).isEqualTo(2);
              assertThat(projectListResponses.get(1).getProjectName()).isEqualTo("測試項目2");
              assertThat(projectListResponses.get(1).getProjectStatus()).isEqualTo(ProjectStatus.RUNNING.getDesc());
          }
      }
      

      在上述測試類中,兩個測試方法 printAllBean()testListAllProjects() 實現完全一致。在類上方的注解中,我們首先通過

      @SpringBootTest(classes = {
              ProjectListServiceImpl.class,
              MybatisTestConfig.class,
              ProjectConverterImpl.class
      })
      

      分別指定我們需要測試的類 ProjectListServiceImplProjectConverter 接口的實現類 ProjectConverterImpl 以及 Mybatis 配置類 MybatisTestConfig。然后通過 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 讓測試類不加載默認的數據源,而是加載我們在 MybatisTestConfig 中配置的數據源;并通過 @PropertySource("classpath:application.properties") 來指定我們使用的測試配置文件。

      運行測試類,可以看到 testListAllProjects() 同樣可以測試通過,且 printAllBean() 的結果如下:

      ========== Spring Beans Total (27) =========
      name=cn.asuka.itd.testconfig.MybatisConfig#MapperScannerRegistrar#0, class=class org.mybatis.spring.mapper.MapperScannerConfigurer
      name=dataSource, class=class com.alibaba.druid.pool.DruidDataSource
      name=hikariPoolDataSourceMetadataProvider, class=class org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration$$Lambda/0x00000205685d79a0
      name=mybatisConfig, class=class cn.asuka.itd.testconfig.MybatisConfig$$EnhancerBySpringCGLIB$$7108b66c
      name=org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory, class=class org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory
      name=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, class=class org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      name=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration, class=class org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration
      name=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration, class=class org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration
      name=org.springframework.boot.context.internalConfigurationPropertiesBinder, class=class org.springframework.boot.context.properties.ConfigurationPropertiesBinder
      name=org.springframework.boot.context.internalConfigurationPropertiesBinderFactory, class=class org.springframework.boot.context.properties.ConfigurationPropertiesBinder$Factory
      name=org.springframework.boot.context.properties.BoundConfigurationProperties, class=class org.springframework.boot.context.properties.BoundConfigurationProperties
      name=org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor, class=class org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
      name=org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter, class=class org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter$$Lambda/0x00000205685d7bb8
      name=org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, class=class org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration
      name=org.springframework.boot.test.context.ImportsContextCustomizer$ImportsCleanupPostProcessor, class=class org.springframework.boot.test.context.ImportsContextCustomizer$ImportsCleanupPostProcessor
      name=org.springframework.boot.test.mock.mockito.MockitoPostProcessor, class=class org.springframework.boot.test.mock.mockito.MockitoPostProcessor
      name=org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor, class=class org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor
      name=org.springframework.context.annotation.internalAutowiredAnnotationProcessor, class=class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
      name=org.springframework.context.annotation.internalCommonAnnotationProcessor, class=class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
      name=org.springframework.context.annotation.internalConfigurationAnnotationProcessor, class=class org.springframework.context.annotation.ConfigurationClassPostProcessor
      name=org.springframework.context.event.internalEventListenerFactory, class=class org.springframework.context.event.DefaultEventListenerFactory
      name=org.springframework.context.event.internalEventListenerProcessor, class=class org.springframework.context.event.EventListenerMethodProcessor
      name=projectConverterImpl, class=class cn.asuka.itd.converter.ProjectConverterImpl
      name=projectListServiceImpl, class=class cn.asuka.itd.project.service.impl.ProjectListServiceImpl
      name=projectMapper, class=class jdk.proxy2.$Proxy84
      name=spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties, class=class org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
      name=sqlSessionFactory, class=class org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
      

      可以看到只加載了 27 個 Bean,大大減少了 Bean 的加載數量,對測試運行速度提升也有幫助。

      總結

      如果需要指定需要測試的 Bean 及其依賴,而不是加載完整的上下文環境,可以在 @SpringBootTest 注解的 classes 參數中配置需要測試及依賴的類或對象。如果遇到不是項目中自己寫的或者可以自動生成的實現類,可以通過配置 @TestConfiguration 的方式,在測試配置中注冊相關的 Bean。最終做到縮小測試范圍,提高測試運行效率。

      posted @ 2025-07-20 09:55  飛鳥_Asuka  閱讀(254)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 漂亮的保姆hd完整版免费韩国| 欧美和黑人xxxx猛交视频| 亚洲 日韩 国产 制服 在线| 热久久这里只有精品国产| 97国产精品人人爽人人做| 巴马| 风流少妇又紧又爽又丰满| 一区二区三区在线色视频| 黄色免费在线网址| 狠狠色狠狠色综合日日不卡| 2021国产精品视频网站| 人人爽人人爽人人片av东京热| 69天堂人成无码免费视频| 青草精品国产福利在线视频| 亚洲精品中文综合第一页| 国产精品一区免费在线看| 女人香蕉久久毛毛片精品| 国产精品国产高清国产av| 日韩女同一区二区三区久久 | 美女黄网站人色视频免费国产 | 亚洲国产精品一区二区久久| 五月综合激情婷婷六月| 高清无打码一区二区三区| 日本久久精品一区二区三区| 亚洲高清成人av在线| 国产精品国产三级国快看| 国产一区二区波多野结衣| 亚洲人成亚洲人成在线观看| 91久久夜色精品国产网站| 亚洲一二区在线视频播放| 三级国产在线观看| 午夜国产小视频| 亚洲大尺度视频在线播放| 精品人妻系列无码天堂| 99精品日本二区留学生| 韩国三级网一区二区三区| 色偷偷女人的天堂亚洲网| 欧美色丁香| 国产一区二区三区不卡观| 国产精品免费看久久久| 亚洲无线看天堂av|