Java单元测试和集成测试实践经验

            • 写在前面的碎碎念

              • 单元测试,集成测试,TDD(测试驱动开发)的重要性,开发和团队都在提。但却很难落地,原因无非,人的惰性和项目管理问题。
              • 个人要从普通到卓越,团队要从作坊到大厂,单元测试和集成测试,TDD,是必不可少的。优秀的开源项目和大厂的核心项目,都很好实践了TDD。
              • TDD,需要转变观念:测试不是测试人员的事儿,开发需要对代码负责,写的代码是可测试的。TDD是一种更严谨的编程思想,能减轻开发维护代码的负担,为后续持续优化提供最起码的保证。单元测试和集成测试,能让开发人员在后续功能开发和重构优化更有信心,减少错误。
              • 单元测试和集成测试,是软件开发最基础的保障手段。更多测试的手段有,自动化测试,流量重放和对比测试等。

              相关书籍和文章

              书籍

              文章

              实践经验

              Gradle配置Test和Jacoco

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              test{
              useJUnitPlatform() //使用JUnit
              failFast=true //快速失败
               // 注意:此处有条件可使用远程docker方案,避免本地机器启动container后资源不足
               if (System.properties['os.name'].toString().toLowerCase().contains('win')) {
                  enviroment "DOCKER_HOST", "tcp://#dockerip:port#"
                }
              }

              jacocoTestReport{
              reports{
              xml.enabled false
              csv.enabled false
              html.destination file("${buildDir}/reports.jacocoHtml") //输出jacoco html文档
              }
              }

              jacocoTestCoverageVerifacation{
              violationRules{
              rule{
              element ='CLASS'
              includes =['com.xxx.yyyy.package'] //覆盖度检查package
              limit{
              minimum=0.9 //单元覆盖度
              }
              }
              }
              }

              test.finalizedBy jacocoTestReport
              test.finalizedBy jacocoTestCoverageVerifacation //执行jacoco报告和覆盖度检查

              使用Spring Test

              引入依赖

              1
              2
              3
              dependencies{
              implementation "org.springfamework.boot:spring-boot-starter-test" //引入 test starter
              }

              抽象测试基类

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              @AutoConfigureMockMvc //开启Spring Mvc Controller层测试,模拟测试HTTP调用
              @ActiveProfiles("test") //开启 test profile,方便日志输出和一些test的定制化
              @SpringBootTest(classes=AppApplication.class) //Spring Test关键注解,完成Spring Context加载和配置工作
              @TestPropertySource(properties={"key=value"}) //测试属性配置
              @ContextConfiguration(classes=IntegrationTestConfiguration.class,initializers=TestApplicationContextInitializer.class) //测试上下文配置和初始化
              @ExtendWith(OutputCaptureExtension.class) //加载测试标准输入的扩展
              @TestExecutionListeners(listeners=XXXTestExecutionListener.class,mergeMode=MergeMode.MERGE_WITH_DEFAULTS) //扩展测试执行Listener
              public class AbstractIntegrationTest{

              //@MockBean和@SpyBean的机制,会刷新Spring Context,为避免不必要的问题。@MockBean和@SpyBean集中抽象到基类,减少Spring Context的刷新
              @MockBean
              protected Service service;
              @SpyBean
              protected Client client;

              @Resource
              private MockMvc mockMvc; //测试Spring Mvc

              protected static final WireMockServer WIRE_MOCK_SERVER=new WireMockServer(new WireMockConfiguration().dynamicPort()) //Wiremock

              static {
              WIRE_MOCK_SERVER.start(); //启动Wiremock
              }

              }

              使用Testcontainers

              为了方便使用Spring Boot Test和Testcontainers,直接使用的Playtika开发的Starter.

              引入依赖

              1
              2
              3
              4
              5
              6
              dependencies{
              implementatin "com.playtika.testcontaners:embedded-mysql"
              implementatin "com.playtika.testcontaners:embedded-redis"
              implementatin "com.playtika.testcontaners:embedded-mongodb"
              implementatin "com.playtika.testcontaners:embedded-rabbitmq"
              }

              配置文件

              以mysql举例

              bootstrap-test.yml

              1
              2
              3
              4
              emebdded:
              mysql:
              enabled: true
              init-script-path: init_mysql.sql

              application-test.yml

              1
              2
              3
              4
              5
              6
              spring:
              datasource:
              driver-class-name: com.mysql.jdbc.Driver
              url: jdbc:mysql://${embbeded.mysql.host}:${embbeded.mysql.port}/${embbeded.mysql.schema}
              username: ${embbeded.mysql.user}
              password: ${embbeded.mysql.password}

              init_msyql.sql

              1
              --数据库初始化脚本,Create Table 和 Insert Sql

              测试用例覆盖范围

              Repository

              测试Repository与DB交互的逻辑

              Service

              测试复杂业务逻辑

              Endpoint

              测试协议转换和入参校验

              测试框架推荐

              Junit5

              Junit5,特性和代码设计上,比Junit4好很多

              AssertJ

              断言,推荐使用AssertJ,特别是它的DSL

              Easy-Random

              很方便的创建和控制Random Object,摆脱手工New Object的麻烦

              Awaitility

              能方便的测试异步代码

              Mockito

              方便Mock,Spy,Verify Bean

               
posted @ 2022-03-30 20:19  木木米  阅读(59)  评论(0)    收藏  举报