• 1
  • 1
分享

为了更真实的展现单元测试的魅力,我使用目前工作中的项目一段代码,你不需要理解具体的业务,只需要了解如何如何写单元测试,以及感受单元测试的魅力就可以了。

这里我们改变一种方式,即我们先写功能代码,再补充单元测试,很多团队都是这样使用,虽然这样并不好,可是很多时候,我们新加入一个团队,不可能负责去做一个新的项目,都是在维护老的项目,并且当时的团队为了更快的编码而没有写单元测试。

功能代码示例

简单了解,只需要注意 条件判断和外部依赖(调用其他类的方法),明白我们的单元测试代码需要覆盖到所有的条件判断,和隔离MOCK 外部依赖。

 /**
     * 根据是否随机设置查询参数
     * @param params
     * @param projectType
     * @param prjId
     * @return
     */
    private  Map<String, Object> setRandParam(Map<String, Object> params,Integer projectType,Integer prjId){
     
     int allocateedAssetCountMin = (Integer)params.get("allocateedAssetCountMin");//资产池列表(前后台)最小项目已匹配资产条数
         int assetRandRoundCount = (Integer)params.get("assetRandRoundCount");//资产池(前后台)随机资产-每次查询多少条
     
         
         if (projectType != null && projectType.equals(ProjectType.LHB.getValue())) {
             prjId = LHB_PROJECTID;
         }
         boolean isRand = false;//是否使用随机债权,默认不使用
         //1.根据prjId确认是否需要使用随机债权
         if (prjId != null) {
             params.put("projectId", prjId);
            
             int investShareCount = investRepository.queryInvtShareCountByPrjId(prjId.toString());
             int allocatedAssetCount = assetRepository.queryAllocatedAssetCountByPrjId(prjId.toString());
             
             if (investShareCount == 0 || allocatedAssetCount < allocateedAssetCountMin) {
                 int randAssetListCount = assetRepository.queryAssetPoolRandListCount(params);//随机债权数据总条数
                 isRand = true;
                 Integer begin = getPoolRandListBegin(randAssetListCount, prjId, PRIME_NUMBER, assetRandRoundCount);
                 Integer end = begin + assetRandRoundCount;
                 params.put("begin", begin);
                 params.put("end", end);
             }
         }
   
         MapUtil.addValueToMap(params, "isRand", isRand);
   
    return params;
   
    }

这段代码中,有条件判断,和依赖外部(数据库)。我们需要对这个做单元测试,要保证不受外界影响(数据库的结果)所以我们需要 MOCK(模拟外部调用返回的数据)

首先为了能够 运行单元测试我们需要引入 junit(我使用过的是Java,其他语言有其他的测试框架)

因为我们需要 @test 注解,和强大的断言 Assert .

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

为了mock 外部的依赖,我们需要使用mock 工具,这里使用 powerMock, 引入相关的 依赖包(maven搜索,这里不再展示)。

为了能够MOCK ,我们在测试类上面使用注解 @RunWith(PowerMockRunner.class) 代表这个类需要使用mock。

在类中对 测试的类使用 @injectMocks 注解我们测试的类, @mock 注解我们外部依赖的类。

@InjectMocks 
private AssetServiceFacade assetServiceFacade;
@Mock
private InvestRepository investRepository;

测试类的命名要求 类名+Test ,测试的方法 test+方法名,编写的测试用例上面 注解@Test ,junit就知道了这个方法是测试用例(毕竟测试类中还存在一些数据准备的方法)。

现在来看看,测试用代码:

根据功能代码中的条件判断,我们需要写三个用例 (这里被测试代码由于是私有的,编写比较特殊需要使用反射来获取方法,公共方法,直接调用类就可以了。)

##用例一:条件为NULL

@Test
public void  testSetRandParamWithNull() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//准备数据
Map<String, Object> params = Maps.newHashMap();
Integer prjId = null;
Integer projectType = null;
MapUtil.addValueToMap(params, "allocateedAssetCountMin", 500);
MapUtil.addValueToMap(params, "assetRandRoundCount", 3000);
//使用反射,调用方法
Class assetFacade = assetServiceFacade.getClass();
Method setRandParam = assetFacade.getDeclaredMethod("setRandParam",Map.class,Integer.class,Integer.class);
setRandParam.setAccessible(true);
params = (Map<String, Object>) setRandParam.invoke(assetServiceFacade, params,projectType,prjId);
Assert.assertEquals(false, params.get("isRand"));
}

用例二

@Test
public void testSetRandParamWithLHB() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//准备数据
Map<String, Object> params = Maps.newHashMap();
Integer prjId = 3;
Integer projectType = 104040; //灵活宝
MapUtil.addValueToMap(params, "allocateedAssetCountMin", 500);
MapUtil.addValueToMap(params, "assetRandRoundCount", 3000);
//使用反射,调用方法
Class assetFacade = assetServiceFacade.getClass();
Method  setRandParam = assetFacade.getDeclaredMethod("setRandParam", Map.class,Integer.class,Integer.class);
setRandParam.setAccessible(true);
//mock
when(investRepository.queryInvtShareCountByPrjId(anyString())).thenReturn((Integer)20);
when(assetRepository.queryAllocatedAssetCountByPrjId(anyString())).thenReturn(600);
params = (Map<String, Object>) setRandParam.invoke(assetServiceFacade, params,projectType,prjId);
Assert.assertEquals(1, params.get("projectId"));
Assert.assertEquals(false, params.get("isRand"));
}

用例三

@Test
public void testSetRandParamWithRand() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//准备数据
Map<String, Object> params = Maps.newHashMap();
Integer prjId = 3;
Integer projectType = 104040;
MapUtil.addValueToMap(params, "allocateedAssetCountMin", 500);
MapUtil.addValueToMap(params,"assetRandRoundCount", 3000);
//使用反射,调用方法
Class assetFacade = assetServiceFacade.getClass();
Method  setRandParam = assetFacade.getDeclaredMethod("setRandParam", Map.class,Integer.class,Integer.class);
setRandParam.setAccessible(true);
when(investRepository.queryInvtShareCountByPrjId(anyString())).thenReturn(20);
when(assetRepository.queryAllocatedAssetCountByPrjId(anyString())).thenReturn(20);
params = (Map<String, Object>) setRandParam.invoke(assetServiceFacade, params,projectType,prjId);
Assert.assertEquals(true, params.get("isRand"));
}

测试步骤

可以看到,上面三个的用例的大致步骤都是:

  1. 准备数据 (方法的参数)

  2. mock相关的外部依赖,直接给出我们结果,when(调用方法).thenReturn(结果)。 当调用这个方法,直接返回 期望的结果。

  3. 断言判断关键结果是否符合我们的期望。(如果不符合,则修改功能代码,前提保证测试的逻辑是没有问题的。)

运行测试

这个时候在单元测试右键 Run as -> Junit Test ,可以看到结果,我的这个,我已经运行成功,但是不认为这是一次就运行成功。事情都是曲折发展,在你编写单元测试的过程中(我们这种方式先编码,再补单元测试)会经常 修改单元测试代码保证单元测试代码没有问题,如果之后测试还跑不通,那么这个时候大胆的修改代码,因为你有单元测试这个强大的工具保驾护航。

1.png

测试覆盖率

测试完成之后,你还需要保证,你的单元测试代码的没有忘记哪个分支条件,此时使用 检测覆盖率工具 EclEmma

进行覆盖的检查。

安装完 EclEmma ,插件右键 coverage as —> junit test ,可以看到我们的功能代码,有了颜色标记,绿色代表 测试代码覆盖到了,红色代表没有覆盖到,黄色代表没有完全覆盖。

2.png总结

那么现在我们总结一下,如果编写单元测试代码:

  1. 准备 junit 测试框架

  2. 创建测试类

  3. 引入 mock工具,mock外部依赖,我们这里使用 powerMock,你可以选择其他的。

  4. 检测单元测试覆盖率


作者:我爱看明朝

原文链接:https://blog.csdn.net/u013565163/article/details/77017286

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •    初次接触批量测试的小伙伴一定一头雾水,不知从何下手。批量交易是什么?它与联机交易有何不同?批量测试都要关注哪些内容?本文结合实际测试经验,详细介绍批量交易的概念、分类以及功能测试关注点,助力测试小伙伴迈出批量功能测试的第一步。  一、什么是批量交易?  本文所称的批量交易,是与联机交易相对应的概念。联机交易具有实时、快速响应用户请求的特点,接收和处理请求都是实时的,处理完毕后立刻将结果返回客户。批量交易则是在接收请求后,顺序处理,全部处理完毕后再将处理结果统一输出,因此具有处理数据量大,执行时间较长的特点。  一般而言,批量交易通过建立作业计划模板,以人工干预或系统自动调起程序...
            13 13 2950
            分享
          • 软件开发人员通常不会考虑的一种测试形式-人工测试。大多数人都以为,因为程序是为了供机器执行而编写的,那么也该由机器来对程序进行测试。这种想法是有问题的。人工测试方法在暴露错误方面是很有成效的。实际上,大多数的软件项目都应使用到一下的人工测试方法:利用错误列表进行代码检查小组代码走查桌面检查同行评审代码检查:所谓代码检查是以组为单位阅读代码,它是一系列规程和错误检查技术的集合。一个代码检查小组通常由四人组成:协调人:以为称职的程序员该程序的编码人员该程序的设计人员测试专家用于代码检查的错误列表:1、数据引用错误是否有引用的变量未赋值或未初始化下标的值是否在范围之内是否存在非整数下标是否存在虚调用...
            0 0 2504
            分享
          • 无论是什么类型的系统,都可以从测试类型角度来考虑测试。1.系统测试回顾测试阶段:1、单元测试,测试函数/方法,需要知道函数内部的逻辑设计,一般采用的是白盒测试方法。2、集成测试,测试接口,需要知道接口的规格,一般采用的是灰盒测试方法。3、系统测试,测试整个软件,需要知道软件的整体特性,一般采用的是黑盒测试方法。4、验收测试,测试整个软件软件质量模型:1、功能性,对应功能测试2、效率,对应性能测试3、易用性,对应易用性测试4、可靠性,对应可靠性测试5、可移植性,对应兼容性测试针对系统可以有不同的测试类型,一般可以粗分为功能测试和非功能测试。将来工作中主要接触的功能测试、性能测试、兼容性测试等。针...
            0 1 2293
            分享
          • 在当今软件开发领域中,泛型是一种强大的编程特性,它能够在不牺牲类型安全的前提下,实现代码的复用和灵活性。Java作为一种老牌的面向对象编程语言,在其长期的发展过程中,已经积累了丰富的泛型经验和应用场景。而Go语言作为一种相对较新的编程语言,也在不断探索和发展其泛型特性,以满足现代软件开发的需求。本文将对Java和Go语言的泛型进行比较和介绍,探讨它们的实现方式、语法特点以及适用场景,帮助读者更好地理解和应用泛型编程。随着Go语言1.18版本的发布,泛型正式成为了Go语言的一部分,填补了原本的短板。通过引入类型参数,使得函数和数据结构可以接受任意类型的参数,从而提升了代码的可复用性和灵活性。这项...
            0 0 1231
            分享
          • 推荐标签:软件测试技术测试用例测试方法在信息技术外包世界,对一个公司拥有它的应用程序和被其他人开发或者维护的框架是普遍的。当销售商完成这个生意,一个普通的实验是把测试过渡活动当成是一个整体。你如何设计一个传统实验的可接受标准以至于它可测试并且被清晰沟通?作为测试,我们通常讨论当一个应用程序被创建时如何接近测试并且被在我们自己公司内的团队维护,但是我们很少讨论当从一个外包供应商到另一个变化时发生了什么。在信息技术外包的世界,对一个公司拥有它的被其他人开发和维护的应用程序和架构是普遍的。当销售商完成这笔生意,有合同涉及组建管理,实时服务水平,关于发布内容的委员会,等等。一个普通的合同部分是测试交换...
            0 0 2082
            分享
      • 51testing软件测试圈微信