如何在Springboot项目中添加testng+mockito+jacoco单元测试

 

1、前言

在日常开发中,当开发某一个模块或者功能时,首先要考虑的是业务逻辑和业务场景,然后会根据业务逻辑和场景进行代码的编写,这其中可能会牵涉到很多的逻辑判断,必要时可能需要与数据库做交互。

但有时开发人员自己也不知道是否把所有的业务场景都包含进去,是否有些逻辑判断可能压根都没有用到,所以一般在开发之后都需要开发人员先对自己所编写的代码进行自测,测试功能是否通畅等,有些可能需要检测测试代码是否覆盖到所有的逻辑分支上。但是在自测功能的时候,如果功能中有需要跟数据库做交互的部分,那这种自测其实做起来比较麻烦,因为如果与数据库做交互需要配置一系列的东西,实施起来比较的麻烦。所以就诞生了testng+mockito+jacoco单元测试模式。

其中testng的作用类似于Junit4,但是包含了Junit4的所有功能,并在它的基础上衍生了更多的功能。详见:https://blog.csdn.net/asd43211234/article/details/105732874

mockito的作用主要是做一些数据模拟工作,比如模拟某个对象,模拟与数据库交互,模拟某些方法的返回值等。

jacoco的作用主要是检测单元测试是否覆盖了整个要测试的类,哪些逻辑分支没有测试到等,并生成测试报告,通过测试报告可以更直观的看到代码覆盖率的情况。

2、准备工作

1、导入testng、mockito、jacoco的依赖,在要测试的类所在的module模块或者所有module的父module的pom文件中加入以下依赖:

<dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.13.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.28.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-library</artifactId>
            <version>2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>2.0.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-testng</artifactId>
            <version>2.0.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.10.9</version>
            <scope>test</scope>
        </dependency>

2、去掉spring-boot-starter-test依赖,如果要测试的module中引入了spring-boot-starter-test,则需要把之前引入的spring-boot-starter-test依赖注释掉。

3、需要在业务实现模块中添加testng.xml文件和testng-1.0.dtd文件

testng.xml

<!DOCTYPE suite SYSTEM "testng-1.0.dtd" >

<suite name="TestSuite" verbose="1">


	<test name="apiTest">
		<packages>
			<package name="全限定包路径.*"></package>
		</packages>
	</test>
</suite>

testng-1.0.dtd

<!--

Here is a quick overview of the main parts of this DTD.  For more information,
refer to the <a href="http://testng.org">main web site</a>.
                                                      
A <b>suite</b> is made of <b>tests</b> and <b>parameters</b>.
                                                      
A <b>test</b> is made of three parts:                        

<ul>
<li> <b>parameters</b>, which override the suite parameters     
<li> <b>groups</b>, made of two parts                           
<li> <b>classes</b>, defining which classes are going to be part
  of this test run                                    
</ul>
                                                      
In turn, <b>groups</b> are made of two parts:                
<ul>
<li> Definitions, which allow you to group groups into   
  bigger groups                                       
<li> Runs, which defines the groups that the methods     
  must belong to in order to be run during this test  
</ul>
                                                      
Cedric Beust & Alexandru Popescu                      
@title DTD for TestNG                                    
@root suite

-->


<!-- A suite is the top-level element of a testng.xml file                  -->
<!ELEMENT suite (groups?,(listeners|packages|test|parameter|method-selectors|suite-files)*) >

<!-- Attributes: -->
<!--
@attr  name        The name of this suite (as it will appear in the reports)
@attr  junit       Whether to run in JUnit mode.
@attr  verbose     How verbose the output on the console will be.  
                This setting has no impact on the HTML reports.
@attr  parallel   Whether TestNG should use different threads
                to run your tests (might speed up the process)
                Do not use "true" and "false" values, they are now deprecated.
@attr  parent-module A module used to create the parent injector of all guice injectors used
       in tests of the suite
@attr  guice-stage The stage with which the parent injector is created
@attr  configfailurepolicy  Whether to continue attempting Before/After
                Class/Methods after they've failed once or just skip remaining.
@attr  thread-count An integer giving the size of the thread pool to use
                if you set parallel.
@attr  annotations  If "javadoc", TestNG will look for
                JavaDoc annotations in your sources, otherwise it will
                use JDK5 annotations.
@attr  time-out     The time to wait in milliseconds before aborting the
                method (if parallel="methods") or the test (parallel="tests")
@attr  skipfailedinvocationcounts Whether to skip failed invocations.
@attr  data-provider-thread-count An integer giving the size of the thread pool to use
       for parallel data providers.
@attr  object-factory A class that implements IObjectFactory that will be used to
       instantiate the test objects.
@attr allow-return-values If true, tests that return a value will be run as well
-->
<!ATTLIST suite 
    name CDATA #REQUIRED
    junit (true | false) "false"
    verbose CDATA #IMPLIED
    parallel (false | true | none | methods | tests | classes | instances) "none"
    parent-module CDATA #IMPLIED
    guice-stage (DEVELOPMENT | PRODUCTION | TOOL) "DEVELOPMENT"
    configfailurepolicy (skip | continue) "skip"
    thread-count CDATA "5"
    annotations CDATA #IMPLIED
    time-out CDATA #IMPLIED
    skipfailedinvocationcounts (true | false) "false"
    data-provider-thread-count CDATA "10"
    object-factory CDATA #IMPLIED
    group-by-instances (true | false) "false"
    preserve-order (true | false) "true"
    allow-return-values (true | false) "false"
>

<!-- A list of XML files that contain more suite descriptions -->
<!ELEMENT suite-files (suite-file)* >

<!ELEMENT suite-file ANY >
<!ATTLIST suite-file
    path CDATA #REQUIRED
>

<!--
Parameters can be defined at the <suite> or at the <test> level.
Parameters defined at the <test> level override parameters of the same name in <suite>
Parameters are used to link Java method parameters to their actual value, defined here.
-->
<!ELEMENT parameter ANY>
<!ATTLIST parameter
    name CDATA #REQUIRED
    value CDATA #REQUIRED >

<!--
Method selectors define user classes used to select which methods to run.
They need to implement <tt>org.testng.IMethodSelector</tt> 
-->
<!ELEMENT method-selectors (method-selector*) >
<!ELEMENT method-selector ((selector-class)*|script) >
<!ELEMENT selector-class ANY>
<!ATTLIST selector-class
    name CDATA #REQUIRED
  priority CDATA #IMPLIED
>
<!ELEMENT script ANY>
<!ATTLIST script
    language CDATA #REQUIRED
>

<!--
A test contains parameters and classes.  Additionally, you can define additional groups ("groups of groups")
-->

<!ELEMENT test (method-selectors?,parameter*,groups?,packages?,classes?) >

<!--
@attr  name         The name of this test (as it will appear in the reports)
@attr  junit        Whether to run in JUnit mode.
@attr  verbose      How verbose the output on the console will be.
                This setting has no impact on the HTML reports.
                Default value: suite level verbose.
@attr  parallel     Whether TestNG should use different threads
                to run your tests (might speed up the process)
                Do not use "true" and "false" values, they are now deprecated.
@attr  thread-count An integer giving the size of the thread pool to be used if
                parallel mode is used. Overrides the suite level value.
@attr  annotations  If "javadoc", TestNG will look for
                JavaDoc annotations in your sources, otherwise it will
                use JDK5 annotations.
@attr  time-out     the time to wait in milliseconds before aborting
                the method (if parallel="methods") or the test (if parallel="tests")
@attr  enabled      flag to enable/disable current test. Default value: true 
@attr  skipfailedinvocationcounts Whether to skip failed invocations.
@attr preserve-order If true, the classes in this tag will be run in the same order as
found in the XML file.
@attr allow-return-values If true, tests that return a value will be run as well
-->
<!ATTLIST test
    name CDATA #REQUIRED 
    junit (true | false) "false"
    verbose  CDATA #IMPLIED
    parallel  (false | true | none | methods | tests | classes | instances) #IMPLIED
    thread-count CDATA #IMPLIED
    annotations  CDATA #IMPLIED
    time-out CDATA #IMPLIED
    enabled (true | false) #IMPLIED
    skipfailedinvocationcounts (true | false) "false"
    preserve-order (true | false) "true"
    group-by-instances (true | false) "false"
    allow-return-values (true | false) "false"
>

<!--
Defines additional groups ("groups of groups") and also which groups to include in this test run
-->
<!ELEMENT groups (define*,run?,dependencies?) >

<!ELEMENT define (include*)>
<!ATTLIST define
    name CDATA #REQUIRED>

<!-- Defines which groups to include in the current group of groups         -->
<!ELEMENT include ANY>
<!ATTLIST include
    name CDATA #REQUIRED
    description CDATA #IMPLIED
    invocation-numbers CDATA #IMPLIED>

<!-- Defines which groups to exclude from the current group of groups       -->
<!ELEMENT exclude ANY>
<!ATTLIST exclude
    name CDATA #REQUIRED>

<!-- The subtag of groups used to define which groups should be run         -->
<!ELEMENT run (include?,exclude?)* >

<!ELEMENT dependencies (group*)>

<!ELEMENT group ANY>
<!ATTLIST group
    name CDATA #REQUIRED
    depends-on CDATA #REQUIRED>

<!-- The list of classes to include in this test                            -->
<!ELEMENT classes (class*,parameter*) >
<!ELEMENT class (methods|parameter)* >
<!ATTLIST class
    name CDATA #REQUIRED >

<!-- The list of packages to include in this test                           -->
<!ELEMENT packages (package*) >
<!-- The package description. 
     If the package name ends with .* then subpackages are included too.
-->
<!ELEMENT package (include?,exclude?)*>
<!ATTLIST package
    name CDATA #REQUIRED >

<!-- The list of methods to include/exclude from this test                 -->
<!ELEMENT methods (include?,exclude?,parameter?)* >

<!-- The list of listeners that will be passed to TestNG -->
<!ELEMENT listeners (listener*) >

<!ELEMENT listener ANY>
<!ATTLIST listener
    class-name CDATA #REQUIRED >

4、需要在业务实现模块pom.xml文件中添加插件maven-surefire-pluginjacoco-maven-plugin

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.10</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <configuration>
                     <includes>
                        <include>com/saicmotor/telematics/icar/service/vehicle/api/impl/**</include>
                    </includes>

                </configuration>


            </plugin>

3、编写测试类

public class ResponseErrorLogApiImplTest {

    //要进行功能测试和查看代码覆盖率的实现类
    @InjectMocks
    private ResponseErrorLogApiImpl responseErrorLogApi;
    //与数据库交互的service方法
    @Mock
    private TbResponseErrorLogService tbResponseErrorLogService;


    @BeforeTest
    public void initMocks() throws Exception {
        System.out.println("BeforeTest");
        // 2 初始化当前测试类所有@Mock注解模拟对象
       MockitoAnnotations.initMocks(this);
    }
    
    //导入testng下的test注解包
    @Test
    public void testSaveErrorLog(){
        LogRequest logRequest = new LogRequest();
        logRequest.setXXX(XXX);
        logRequest.setYYY(YYY);
        
        //模拟保存数据库成功
        when(tbResponseErrorLogService.save(any())).thenReturn(true);
        //要测试的功能
        Result result = responseErrorLogApi.saveErrorLog(logRequest);
        //比对期望值与实际值是否一致
        Assert.assertEquals(ResultCode.CODE_500,result.getCode());
    }

}

运行Test即可测试某个方法的的结果是否为期望的结果。

4、查看代码覆盖率

在Terminal进入到业务逻辑模块的路径执行mvn clean test或者使用mvn 命令指定具体mvn XXXModule clean test编译整个模块下的Test类。

编译完成后到target目录下找到如下目录,打开index.html,选择对应的包名后选择@InjectMocks标注的类名即可看到代码覆盖率。

从上图可以看到代码覆盖率为88%,逻辑分支覆盖率为83%

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值