美文网首页
Spring + jacoco + powerMock 单元测试

Spring + jacoco + powerMock 单元测试

作者: dynemm | 来源:发表于2021-05-19 01:23 被阅读0次

    一 什么是单元测试

    什么是单元测试呢?单元测试就是针对最小的功能单元编写测试代码。Java程序最小的功能单元是方法,因此,对Java程序进行单元测试就是针对单个Java方法的测试。
    其实在日常的工作中,大部分开发都很容易对项目的单测忽略,甚至因为工期紧张就没有写过单元测试,作者上一家公司就没有写单测的习惯,换了工作之后,这边对单测的要求还是比较高的,新项目行覆盖率需要达到80%,旧的项目行覆盖率需要达到60%,在经过几次磨练之后,接下来就重点的讲一讲如何写单测(其实我也写的不是很好),以及如何知道自己的项目的一个整体的单测覆盖情况,这里面有一些概念啊比如说行覆盖率、方法覆盖率等大家事先先了解一下

    二 Spring + Jacoco + powerMock 单测

    其实写单测很简单,我们最熟悉的就是用大家都很熟悉Junit去编写单测,但是这对一般的方法逻辑是可行的,但是遇到有外部依赖等复杂条件,用Junit去写就不是很方便了。
    一般处理Spring 单测我们一般的做法就是如下启动Spring容器:


    image.png

    其实这样的做法有几个不好的地方哈:
    1.试想,这每个测试方法都需要进行Spring 容器的初始化,这样整一个CI流程跑下来的话,时间就会很长
    2.如果遇到有第三方调用的情况(没有进行mock的情况),本机环境和三方环境不通的情况,很容易就导致了单测的失败
    那么有人就说了,那你第三方的调用数据进行mock不就行了嘛,其实是可以的哈,其实单测的话 从controller层 到 service层 到 dao、RPC层 都应该进行单测编写,但是实际开发时间都不会允许有这么长的时间去整,所以一般的关注点就是在service层,并且建议controller层不要写太多的业务代码,尽量都放在service层里面。那么service层的代码将那些重要的操作进行mock掉之后,就可以在不启动容器的情况下快速的进行跑单测,但这个不是绝对的哈,根据自己项目的风格来,该启容器还是得起的。
    好嘛,讲了这么多,其实大家很多人都应该知道这些个理论啊,那么接下来就手把手教大家怎么集成并且手机

    三 实战

    首先父pom文件需要引入一些依赖和jacoco的pluin工具

    父pom文件:
            <lombok-version>1.18.16</lombok-version>
            <org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
            <jacoco.version>0.8.0</jacoco.version>
            <powermock-api-version>1.7.4</powermock-api-version>
            <powermock-module-junit4.version>2.0.2</powermock-module-junit4.version>
            <mockito-all.version>${powermock-module-junit4.version}-beta</mockito-all.version>
    
        <dependencies>
            <dependency>
                <groupId>org.jacoco</groupId>
                <artifactId>org.jacoco.agent</artifactId>
                <version>${jacoco.version}</version>
                <classifier>runtime</classifier>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M1</version>
                <type>maven-plugin</type>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    <dependencyManagement>
                <!--单测相关-->
                <dependency>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <groupId>org.springframework.boot</groupId>
                    <version>1.5.22.RELEASE</version>
                </dependency>
                <dependency>
                    <artifactId>junit</artifactId>
                    <groupId>junit</groupId>
                    <scope>test</scope>
                    <version>4.12</version>
                </dependency>
                <dependency>
                    <artifactId>spring-test</artifactId>
                    <groupId>org.springframework</groupId>
                    <version>4.3.18.RELEASE</version>
                </dependency>
                <!--power mock-->
                <dependency>
                    <groupId>org.powermock</groupId>
                    <artifactId>powermock-module-junit4</artifactId>
                    <scope>test</scope>
                    <version>${powermock-module-junit4.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.powermock</groupId>
                    <artifactId>powermock-api-mockito</artifactId>
                    <scope>test</scope>
                    <version>${powermock-api-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.powermock</groupId>
                    <artifactId>powermock-core</artifactId>
                    <version>${powermock-api-version}</version>
                    <scope>test</scope>
                </dependency>
                <dependency>
                    <groupId>org.mockito</groupId>
                    <artifactId>mockito-all</artifactId>
                    <version>${mockito-all.version}</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    <build>
            <plugins>
                <!--      <plugin>-->
                <!--        &lt;!&ndash; The gmavenplus plugin is used to compile Groovy code. To learn more about this plugin,-->
                <!--        visit https://github.com/groovy/GMavenPlus/wiki &ndash;&gt;-->
                <!--        <groupId>org.codehaus.gmavenplus</groupId>-->
                <!--        <artifactId>gmavenplus-plugin</artifactId>-->
                <!--        <version>1.6</version>-->
                <!--        <executions>-->
                <!--          <execution>-->
                <!--            <goals>-->
                <!--              <goal>compile</goal>-->
                <!--              <goal>compileTests</goal>-->
                <!--            </goals>-->
                <!--          </execution>-->
                <!--        </executions>-->
                <!--      </plugin>-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.0.0-M1</version>
                    <configuration>
                        <reportsDirectory>../surefire-reports</reportsDirectory>
                        <systemPropertyVariables>
                            <jacoco-agent.destfile>jacoco.exec</jacoco-agent.destfile>
                        </systemPropertyVariables>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version> <!-- or newer version -->
                    <configuration>
                        <source>1.8</source> <!-- depending on your project -->
                        <target>1.8</target> <!-- depending on your project -->
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                                <version>${lombok-version}</version>
                            </path>
                            <path>
                                <groupId>org.mapstruct</groupId>
                                <artifactId>mapstruct-processor</artifactId>
                                <version>${org.mapstruct.version}</version>
                            </path>
                            <!-- other annotation processors -->
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <version>${jacoco.version}</version>
                    <executions>
                        <execution>
                            <id>default-instrument</id>
                            <goals>
                                <goal>instrument</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>default-restore-instrumented-classes</id>
                            <goals>
                                <goal>restore-instrumented-classes</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    

    哪个模块需要打桩,就把dependencyManagement 里面的jar包引入到模块里面就可以了
    接下来就是划重点的地方了,项目里面需要重新起一个模块用于进行单测结果的收集和聚合,如果想对service层的代码就行聚合的话,添加这个模块的依赖就行了,初次以外,如果有一些文件你觉得不需要进行单测手机,以免影响到你的整个项目的测试覆盖率,那么你可以自定义的去排除这些文件夹或者文件 话不多说 上代码

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>dietetic</artifactId>
            <groupId>com.dynelm</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>dynelm-dietetic-test</artifactId>
    
        <dependencies>
            <dependency>
                <artifactId>dynelm-dietetic-service</artifactId>
                <groupId>com.dynelm</groupId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <version>0.8.0</version>
                    <configuration>
                        <fileSets>
                            <fileSet>
                                <directory>..</directory>
                                <include>**/*.exec</include>
                            </fileSet>
                        </fileSets>
    
    
                        <!--                    <includes>-->
                        <!--                        <include>com/bytedance/cg/gcrm/contact/**</include>-->
                        <!--                    </includes>-->
                        <!--                    <excludes>-->
                        <!--                        &lt;!&ndash;thrift生成的文件过滤掉&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/remote/**</exclude>-->
                        <!--                        &lt;!&ndash;mybatis生成的文件过滤掉&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/repository/mapper/**</exclude>-->
                        <!--                        &lt;!&ndash; mq不支持 &ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/mq/**</exclude>-->
                        <!--                        &lt;!&ndash;登录过滤器过滤掉&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/web/interceptor/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/listener/**</exclude>-->
                        <!--                        &lt;!&ndash;common定义去掉,不是业务逻辑&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/web/common/**</exclude>-->
                        <!--                        &lt;!&ndash;启动器不单测&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/web/LocalLifeHttpServiceApplication**</exclude>-->
                        <!--                        &lt;!&ndash;demo&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/web/controller/DemoController.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/web/controller/TestController.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/service/impl/TestService.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/filter/TccLogbackFilter.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/es/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/adapter/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/rmq/consumer/**</exclude>-->
                        <!--                        &lt;!&ndash;这三个都带有导入性质的,写单测成本太高,保证controller和service可用即可&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/repository/impl/LeadRepositoryImpl.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/repository/impl/IMaterialDataSummaryRepositoryImpl.class</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/repository/impl/BaseAccountRoleRepositoryImpl.class</exclude>-->
                        <!--                        &lt;!&ndash;bsm的不是所有方法都用,暂时规避调&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/adapter/BsmAdapter.class</exclude>-->
                        <!--                        &lt;!&ndash; 账号同步的定时任务不测试 &ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/service/impl/AccountSaleBaseRefService.class</exclude>-->
                        <!--                        &lt;!&ndash; 数据导入脚本 &ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/listener/IndexOrNameDataListener.class</exclude>-->
                        <!--                        &lt;!&ndash;转换器不测试&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/adapter/converter/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/infrastructure/repository/converter/**</exclude>-->
                        <!--                        &lt;!&ndash;工具类和配置不测试&ndash;&gt;-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/utils/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/entity/query/**</exclude>-->
                        <!--                        <exclude>com/bytedance/cg/locallife/base/conf/**</exclude>-->
                        <!--                        &lt;!&ndash;自动生成的不测试&ndash;&gt;-->
                        <!--                        <exclude>**.equals**</exclude>-->
                        <!--                        <exclude>**.hashCode**</exclude>-->
                        <!--                        <exclude>**.toString**</exclude>-->
                        <!--                        <exclude>**.canEqual**</exclude>-->
                        <!--                        &lt;!&ndash;dto对象过滤掉&ndash;&gt;-->
                        <!--                        <exclude>**/dto/**</exclude>-->
                        <!--                        <exclude>**Dto.class</exclude>-->
                        <!--                        <exclude>**/vo/**</exclude>-->
                        <!--                        <exclude>**/req/**</exclude>-->
                        <!--                    </excludes>-->
                    </configuration>
                    <executions>
                        <execution>
                            <phase>verify</phase>
                            <goals>
                                <goal>merge</goal>
                                <goal>report-aggregate</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    至此,整个收集的就搭建完了,我来测试一下,编写好单测后,输入命令行:mvn clean verify -Dmaven.test.failure.ignore=true 进行收集,我们会在你刚才新建的收集模块的target里面找到一个index.xml文件,右键运行就可以看到整个项目的单测情况,如下所示:


    image.png

    结果如图:


    image.png
    红色标注的就是整个项目的行覆盖率,每个包你都能点进行看那些代码覆盖到了,哪些代码没覆盖到,根据这些来进行单测补充。
    好的,下次会给大家详细介绍一下,怎么去写单测的代码!

    相关文章

      网友评论

          本文标题:Spring + jacoco + powerMock 单元测试

          本文链接:https://www.haomeiwen.com/subject/qefufktx.html