51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 394|回复: 0
打印 上一主题 下一主题

[原创] 单元测试覆盖率如何达到80%以上(上)

[复制链接]
  • TA的每日心情
    无聊
    4 天前
  • 签到天数: 941 天

    连续签到: 3 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-3-30 10:54:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    什么是单元测试
      单元测试(unit testing)是指对软件中的最小可测试单元进行检查和验证。它是软件测试中的一种基本方法,也是软件开发过程中的一个重要步骤。
      单元测试的目的是在于确保软件的每个独立模块都被正确地测试,并且没有潜在的缺陷或漏洞。在单元测试中,需要对每个模块进行测试,以确保它们能够按照预期的方式工作,并且没有任何错误或漏洞。
      单元测试通常包括以下几个步骤:
      1、确定测试范围:在开始测试之前,需要确定测试的范围,即要测试的功能或模块。
      2、编写测试用例:根据确定的测试范围,编写测试用例,这些用例应该覆盖软件中的每个模块。
      3、执行测试用例:使用测试工具(如JUnitTestNG、Mock等)执行测试用例,以确保每个模块都按照预期的方式工作。
      4、分析测试结果:在测试完成后,需要分析测试结果,以确定是否存在缺陷或漏洞。
      5、修复缺陷或漏洞:如果发现缺陷或漏洞,需要修复它们,以确保软件的质量。
      单元测试的意义
      提高代码质量:通过编写单元测试,可以发现代码中的错误和漏洞,从而提高代码的质量。
      提高开发效率:通过编写单元测试,可以快速地发现代码中的问题,从而减少测试时间,提高开发效率。
      降低维护成本:通过编写单元测试,可以及早地发现代码中的问题,从而减少维护成本,提高代码的可维护性。
      提高代码可靠性:通过编写单元测试,可以检查代码中的错误和漏洞,从而提高代码的可靠性,减少故障的发生。
      前言:
      看完上面的就知道什么时候或者为什么要编写单元测试了。其他的我们不多说了,直接进入实战操作,这次使用的是springboot+Mockito框架,在最后会指出一些小技巧和bug。
      实战
      一.Mockito的jar包导入:
      <dependencies>
        <!-- 单元测试 -->
      <dependency>
      <groupId>org.jmockit</groupId>
      <artifactId>jmockit</artifactId>
      <version>1.38</version>
      <scope>test</scope>
      </dependency>
      <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
      </dependency>
      <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>2.0.2</version>
      <scope>test</scope>
      <exclusions>
      <exclusion>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      </exclusion>
      <exclusion>
      <groupId>org.objenesis</groupId>
      <artifactId>objenesis</artifactId>
      </exclusion>
      </exclusions>
      </dependency>
      <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito2</artifactId>
      <version>2.0.2</version>
      <scope>test</scope>
      <exclusions>
      <exclusion>
      <artifactId>mockito-core</artifactId>
      <groupId>org.powermock</groupId>
      </exclusion>
      <exclusion>
      <artifactId>mockito-core</artifactId>
      <groupId>org.mockito</groupId>
      </exclusion>
      </exclusions>
      </dependency>
      <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>3.9.0</version>
      <scope>test</scope>
      </dependency>
        </dependencies>


      <build>
      <plugins>
        <!-- 单元测试 -->
      <plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <version>0.8.7</version>
      <executions>
      <execution>
      <id>prepare-agent</id>
      <goals>
      <goal>prepare-agent</goal>
      </goals>
      </execution>
      <execution>
      <id>report</id>
      <phase>test</phase>
      <goals>
      <goal>report</goal>
      </goals>
      </execution>
      </executions>
      </plugin>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.12.2</version>
      <configuration>
      <testFailureIgnore>true</testFailureIgnore>
      </configuration>
      </plugin>
      </plugins>
      <!-- 修改对应名称 -->
      <finalName>iot-open-api</finalName>
      </build>



      没法上传pom文件
      二.创建单元测试类
      package com.shimao.iot.iotopenapi.service.impl;
      import com.shimao.iot.common.bean.AttributesEntity;
      import com.shimao.iot.common.bean.DeviceDataEntity;
      import com.shimao.iot.common.bean.DeviceEntity;
      import com.shimao.iot.common.bean.DeviceTypeEntity;
      import com.shimao.iot.common.bean.device.UpdateBatchDeviceAttributeReq;
      import com.shimao.iot.common.bean.member.EditShimaoFaceReq;
      import com.shimao.iot.common.bean.member.ShimaoFaceReq;
      import com.shimao.iot.common.elk.entity.DeviceReportEntity;
      import com.shimao.iot.common.entity.ResultVO;
      import com.shimao.iot.common.model.device.req.DeviceReportHeartReq;
      import com.shimao.iot.common.model.device.req.DeviceReportInfoReq;
      import com.shimao.iot.common.model.face.req.AlarmInfo;
      import com.shimao.iot.common.model.face.req.DeviceStateReq;
      import com.shimao.iot.common.model.face.req.FaceCollectInfoReq;
      import com.shimao.iot.common.model.face.req.FaceCollectReq;
      import com.shimao.iot.common.model.face.req.PassRecord;
      import com.shimao.iot.iotopenapi.bean.dto.device.DeviceExtDataEntity;
      import com.shimao.iot.iotopenapi.kafka.KafkaProducer;
      import com.shimao.iot.iotopenapi.serviceFeign.DeviceFeignService;
      import com.shimao.iot.iotopenapi.serviceFeign.ElkClient;
      import com.shimao.iot.iotopenapi.serviceFeign.MemberClient;
      import com.shimao.iot.iotopenapi.serviceFeign.OssService;
      import org.junit.Assert;
      import org.junit.Before;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.mockito.InjectMocks;
      import org.mockito.Mock;
      import org.mockito.Mockito;
      import org.mockito.MockitoAnnotations;
      import org.powermock.modules.junit4.PowerMockRunner;
      import org.springframework.beans.factory.annotation.Value;
      import [url=]java[/url].util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      /**
       *
       * @Author zhangtonghao
       * @create 2023-01-31 14:41
       */
      @RunWith(PowerMockRunner.class)
      public class DeviceReportServiceImplTest {
          @Mock
          private DeviceFeignService deviceFeignService;
          @Mock
          private OssService ossService;
          @InjectMocks
          com.shimao.iot.iotopenapi.service.Impl.DeviceReportServiceImpl deviceReportServiceImpl;
          static {
              System.setProperty("env", "baseline");
          }
          @Before
          public void setUp() {
              MockitoAnnotations.openMocks(this);
          }
          @Test
          public void testDeviceLockState() {
              // Setup
              DeviceStateReq req = new DeviceStateReq();
              req.setEntityCode("entityCode");
              req.setGwCode("gwCode");
              req.setTimestamp("timestamp");
              req.setReqId("reqId");
              req.setTypeCode("typeCode");
              req.setOpt("opt");
              req.setMsgType("msgType");
              //存取code
              AlarmInfo alarmInfo = new AlarmInfo();
              alarmInfo.setCode("10000");
              alarmInfo.setMessage("message");
              alarmInfo.setPictureUrl("pictureUrl");
              req.setAlarmInfo(alarmInfo);
              req.setAttributesEntities(Arrays.asList(new AttributesEntity(0L, 0L, "attributeCode", "value")));
              PassRecord passRecord = new PassRecord();
              passRecord.setId("id");
              passRecord.setRecordId("recordId");
              passRecord.setName("name");
              passRecord.setPassPhoto("passPhoto");
              passRecord.setPassMode("passMode");
              passRecord.setResultType(0);
              passRecord.setPassTime("passTime");
              passRecord.setCode("10000");
              passRecord.setPersonType(0);
              req.setPassRecords(Arrays.asList(passRecord));
              // Configure DeviceFeignService.queryDeviceInfoByDeviceCode(...).
              DeviceExtDataEntity deviceExtDataEntity = getDeviceExtDataEntity();
      
              Mockito.when(deviceFeignService.queryDeviceInfoByDeviceCode(Mockito.any())).thenReturn(deviceExtDataEntity);
              Mockito.when(deviceFeignService.updateAttributesById(Mockito.any())).thenReturn(ResultVO.ok(null));
              Mockito.when(ossService.uploadByBase64(Mockito.any())).thenReturn(ResultVO.ok(null));
              // Run the test
              ResultVO result = deviceReportServiceImpl.deviceLockState(req);
              // Verify the results
              Assert.assertNotNull(result);
          }

          private DeviceExtDataEntity getDeviceExtDataEntity() {
              AttributesEntity attributesEntity = new AttributesEntity();
              attributesEntity.setEntityId(11L);
              attributesEntity.setAttributeCode("11L");
              attributesEntity.setValue("11");
              List<AttributesEntity> attributes = new ArrayList<>();
              attributes.add(attributesEntity);
              DeviceExtDataEntity deviceExtDataEntity = new DeviceExtDataEntity();
              deviceExtDataEntity.setChannel(1);
              deviceExtDataEntity.setSpaceId(11L);
              deviceExtDataEntity.setTypeCode("1");
              deviceExtDataEntity.setComdTopic("1");
              deviceExtDataEntity.setDeviceCode("11");
              deviceExtDataEntity.setDeviceId(11L);
              deviceExtDataEntity.setDeviceName("11L");
              deviceExtDataEntity.setDiff("11");
              deviceExtDataEntity.setPanType("11");
              deviceExtDataEntity.setSourcePlatform("11");
              deviceExtDataEntity.setSpaceId(11L);
              deviceExtDataEntity.setIconUrl("11");
              deviceExtDataEntity.setRootSpaceId(11L);
              deviceExtDataEntity.setAttributesEntities(attributes);
              deviceExtDataEntity.setStatus(1);
              return deviceExtDataEntity;
          }
      }


      三.常用注解了解
      简洁版:
      @InjectMocks:通过创建一个实例,它可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
      @Mock:对函数的调用均执行mock(即虚假函数),不执行真正部分。
      @Spy:对函数的调用均执行真正部分。(几乎不会使用)
      Mockito.when( 对象.方法名() ).thenReturn( 自定义结果 ):后面自定返回结果,需要和方法返回结果类型一致,
      Mockito.any():用于匹配任意类型的参数
      详细版:
      @RunWith(PowerMockRunner.class)
      是JUnit的一个Runner,PowerMockRunner通过使用Java Instrumentation API和字节码操作库ByteBuddy,使得Java类和对象避免了Java单继承和final类限制,能够进行更灵活的mock测试。在JUnit中使用@RunWith(PowerMockRunner.class)来运行单元测试,可以使用PowerMock框架进行Mocking、Stubbing和Verification等操作,它可以完全模拟一个无法模拟的对象,如静态方法、final类、private类等。此外,PowerMockRunner还支持EasyMock和Mockito等常见的Mock技术。
      @Mock
      所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:
      1. 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等。
      2. 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作。
      是一个Mockito框架中的注解,它可以用于创建一个模拟对象。使用@Mock注解可以使测试代码更简洁并且便于阅读,无需手动创建模拟对象。
      具体来说,@Mock注解通常用于测试类中需要测试的类所依赖的对象。当我们使用@Mock注解标注一个对象时,这个对象的行为可以被模拟,以便对测试目标类进行测试。在对模拟对象进行测试时,我们可以设定模拟对象的返回值或行为,并用这些值来测试测试目标类的行为。
      需要注意的是,使用@Mock注解必须先使用Mockito.mock()初始化Mock对象。通常,我们会在测试类的setUp()方法中使用@Mock注解来初始化Mock对象,这样测试类的每个测试方法都可以使用它。
      同时还需要注意,@Mock注解只是用于创建一个模拟对象,在使用这个对象进行测试时,需要手动设定其返回值或行为。
      @InjectMocks
      是Mockito框架中的注解。它可以自动为测试类中声明的变量注入被mock的对象。使用@InjectMocks注解可以让测试代码更加简洁和易读,无需手动创建对象。
      具体来说,@InjectMocks注解通常用于注入一个类的成员变量,这个成员变量通常是另外一个类的实例(被mock的对象)。在测试类实例化时,Mockito会自动查找这个被mock对象的实例,然后把它注入到@InjectMocks注解标识的变量中。
      需要注意的是,@InjectMocks注解仅仅用于自动注入成员变量。如果需要mock类的方法,应该使用@Mock注解。
      同时,如果一个类里面有多个同类型的成员变量,需要手动使用@Qualifier注解来指定需要注入的对象。当然你也可以通过不同名称来区分同一类型的变量。
      Mockito.when()
      是Mockito框架中的一个方法,它可以被用于设定模拟对象的行为。该方法通常和@Mock或@Spy注解一起使用,用于模拟对象的行为并指定返回值或者其他行为。
      具体来说,Mockito.when()方法接受两个参数,一个是模拟对象的方法调用,另一个是指定的行为或返回值。当模拟对象的方法被调用时,Mockito就会按照when()方法中指定的方式进行处理。例如,可以使用Mockito.when()方法来模拟一个方法的返回值。
      需要注意的是,Mockito.when()方法并不会真正地执行方法,而是返回了一个指定的返回值或设定的行为,用于在测试中进行验证。同样需要注意的是,如果模拟对象的方法参数不是一个基本类型或String,则需要手动匹配参数。
      Mockito.any()
      它可以用于匹配任意类型的参数。在测试代码中,当需要匹配方法的参数但不关心具体的参数值时,可以使用Mockito.any()方法来匹配参数。
      具体来说,Mockito.any()方法可以用于模拟对象的方法调用或验证方法调用时的参数匹配。
      需要注意的是,当使用Mockito.any()方法时,需要确保模拟方法的返回值与模拟方法的参数类型兼容。

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-5-4 21:41 , Processed in 0.063010 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表