• 0
  • 0
分享

  1 引言

  1.1背景

  随着Android应用越来越壮大,对应代码量显著增加,代码逻辑也日趋复杂,此时需要采取一定的行动去保证代码质量,减少逻辑漏洞等,于是严格地执行单元测试编写任务,将单元测试落实到平常开发和维护任务当中去,就是很重要的一环,不可忽视。

  然而,很多应用开发者之前并未编写过单元测试代码,那么如果有一篇通俗易懂并带有操作步骤的文章,能帮助应用开发者完成从单元测试小白到入门的过渡,就再好不过了,于是本文就是在此情况写就的,如有不好之处,请多多包涵,谢谢。

  1.2 术语和缩略语

1-1.png


  2 闲谈单测

  2.1 说说我理解的单测

  2.1.1 对测试金字塔的理解

1-2.jpg


  本文所指的单测,是金字塔最底层占70%的小型测试(单测) Unit tests,不包括最顶层占10%的大型测试(UI 测试) UI Tests,对于中间层20%的集成测试(集测),需看情况去做。

  另外,集成测试更多是指验证一整个执行流程,而单测验证某个行为或逻辑,可以这么理解:集成测试验证执行流程时,会走单测验证过的某个行为或逻辑,即:单测可能是集成测试的一部分(不完全正确,但可以这么理解)。

  比如,Android 中需要在 Activity 显示一张网络图片,单测需验证网络请求图片是否成功,集测需验证从打开 Activity 到显示图片这一过程是否都按预期在执行,UI 测试验证 Activity 显示的图片是否如预期一样。

  2.1.2 为什么要做单测?

  除了引言中背景提到的单测必要性之外,还有如下2点理由。

  (1)一种代码验证,提高对代码的自信度

  当我们对类函数代码做了修改或者重构,只需要再跑一遍单元测试

  如果通过,说明我们的修改不会对函数原有的行为造成影响

  如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。

  (2)驱动代码重构设计,代码重构质量的验证

   刚开始,已有的代码可能基本没有办法写单测,会形成重构驱动,重构的过程会让代码逐渐具备良好的可测性

  如果可以针对代码方便快速地,在无需做任何配置工作的情况下,编写一系列互相独立且稳定运行的测试,那么该代码就具备了可测性

  具备可测性的代码必然是经过了良好设计的,形成可测性代码的过程,也就是编码技能提升的过程。

  2.1.3 需要写 UI 测试吗?

  Android 应用从广义来说,属于大前端,也就意味着 UI 变动会很频繁。且对于 UI 问题,应用开发者是很容易就能发现的,一般也比较容易解决。那么,针对 UI 进行测试其实是需要的,但没那么必要,可以不用写 UI 测试。

  不用写 UI 测试,除了上述提到的 2 个理由,另外还有这 2 个理由:Espresso 单测比较简单;测开会做 UI 测试。将重心放在单测上才是王道。

  2.1.4 单测与重构

  在没有进行合理的代码解耦前,就马上的进行单测编写,最终导致的有可能是编写不下去、用例难以维护、阅读性差等。

  根本的原因其实是代码的耦合度过高,功能类与协作类之间是强引用关系(这里是指架构设计上的说法,非内存引用),当需要模拟协作类的一些返回结果来进行测试时,无法替换或者模拟(Mock)。层级间、功能类间以接口的形式访问是一个较优的单测方式。     

  没写过单测的童鞋可能不知道,其实重构也是单测的一部分,切记不要在本就不优雅的代码上写单测,请先重构。单测为代码质量保驾护航,重构提升代码质量和自我编程能力。

  2.1.5 TDD

  项目如果走的敏捷开发,会涉及到测试驱动开发这一设计方法论,但说实话,在写代码前先写好测试代码,这对开发者能力要求很高,且目前国内开发环境其实对这块的理解并不如理想之见,暂无需考虑。

  2.1.6 单测写在哪?

  首先,我们暂不写 UI 测试,只做单测,那么只需要在 src/test/java/包名/ 下写即可,一般来说可以不用在 src/androidTest/java/包名/ 下写单测代码。

  其次,针对于 Android 应用,你可以在每个 Module 下都为其编写单测代码,然后统计各个 Module 的覆盖率,最后求一个平均覆盖率,即为整个应用的覆盖率。但,我们知道这其实是比较麻烦的,每次统计时都需要去计算每个 Module 的覆盖率然后求平均,当然如果用 jacoco 可以优雅自动统计的话就另当别论。

  所以,为了方便统计单测覆盖率,本文暂推荐在指定 Module 的 src/test/java/包名/ 下写所有 Module 的单测代码,这个指定 Module 可以是 app 模块,也可以是新建的专为写单测的 unit_test 模块。

  2.1.7 单测三段式

   经典的单测三段式:模拟前提、执行语句、断言结果。有时会把这三者的部分或全部合在一起,如:Assert.assertEquals(4, 2+2),就把三者融合在一起。

  2.1.8 一个简单的测试例子

  我们代码中经常会有日期工具类,下面是对有效日期判断的方法进行的测试例子。

  (1)有效日期判断的源码

  object DateUtils {
      fun getValidDate(milliseconds: Long): Long {
          var validDate = milliseconds
          val timeInMillis = Calendar.getInstance(Locale.US).timeInMillis
          if (milliseconds <= 0L || milliseconds > timeInMillis) {
              validDate = timeInMillis
          }
          return validDate
      }
  }

  (2)测试代码

 @Test
  fun testGetValidData() {
      // 1、模拟前提
      val currentTimeMillis1 = System.currentTimeMillis()
      // 2、执行语句
      val validDate1 = DateUtils.getValidDate(-1)
      // 3、断言结果
      Assert.assertTrue(validDate1 in (currentTimeMillis1 - 1000)..(currentTimeMillis1 + 1000))
   
      val currentTimeMillis2 = System.currentTimeMillis()
      val validDate2 = DateUtils.getValidDate(0)
      Assert.assertTrue(validDate2 in (currentTimeMillis2 - 1000)..(currentTimeMillis2 + 1000))
   
      val currentTimeMillis3 = System.currentTimeMillis()
      val validDate3 = DateUtils.getValidDate(currentTimeMillis3 + 100000)
      Assert.assertTrue(validDate3 in (currentTimeMillis3 - 1000)..(currentTimeMillis3 + 1000))
   
      val milliseconds4 = System.currentTimeMillis() - 100000
      val validDate4 = DateUtils.getValidDate(milliseconds4)
      Assert.assertEquals(milliseconds4, validDate4)
  }

  2.2 单测针对于哪些代码进行 

  单测是指对软件中的最小可测试单元进行检查和验证,要以类功能作为测试目标的单个或者一连串的函数测试,也就是说,单测可以是对某个类的具体函数的功能、内部逻辑进行验证。

  而针对代码复杂性和依赖性,有如下图的原则描述可参考:

1-3.png

  ·复杂依赖少:适合写单测

  · 复杂依赖多:重构减少依赖,变成复杂依赖少,然后写单测

  · 简单依赖少:看情况写

  · 简单依赖多:不用重构,不用单测

  这里对代码简单依赖多的情况多提一嘴,对它们写单测意义并不大,不要为了提高单测覆盖率,而花费很多时间和精力去写单测,这样得不偿失。

  2.3 测试用例该如何设计

  2.3.1 确保原因与影响清晰

  模拟前提条件和测试断言之间相隔了200行,这很难知道这断言是否正确,可读性差。

1-4.png

  确保模拟前提与验证结果相近,查看问题时能快速定位。

1-5.png

  2.3.2 只验证有状态变化的函数调用

  对函数执行过程中验证不带来状态发生改变的函数是无意义的,例如isUserActive、getPermission、isVAlidPermission等都是对程序没有发生状态变化的,验证是否这类函数是否执行,只会增加后续代码修改带来的不稳定。

1-6.png

  正确应该只验证addPermission是否执行。

 1-7.png

  2.3.3 测试函数名应该有描述性

  测试函数名应该具备可直接理解该测试用例的意图和验证结果,如下这种写法,很难让人知道测试目的是为了验证三次输入错误。

1-8.png

  正确应该命名为:should_LockOutUser_when_ThreeInvalidLoginAttempts 

  should_期望结果_when_测试场景。确保测试用例名字中包括被测试的场景和期望的输出。

  2.4 单测框架

  Junit4 + Mockito(powerMock)+ robolectric

  简单单测 + 模拟难以实例化的类 + 实现 Android 框架(更多内容请参考相应框架学习)



作者:Swuagg    

来源:http://www.51testing.com/html/56/n-7793556.html

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

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   前言  通常在接口自动化中,经常会参数关联的问题,那么什么是参数关联?  参数关联就是上一个接口的返回值会被下一个接口当做参数运用,其中Python中可以实现参数关联的方法有很多种,今天小编给大家介绍下,如何通过Python来实现接口自动化中的参数关联。  UnitTest  虽然说目前Pytest框架比较流向,但是目前应该有绝大部分公司还是在使用UnitTest框架,那么小编先介绍下如何通过UnitTest来实现接口自动化的参数关联。  方法一  下面小编通过测试用例返回参数的形式进行实现参数关联。# coding:utf-8 import requests impo...
            0 0 823
            分享
          • 一、前言2017年中旬,有幸接手了公司新产品的测试,领导通知说该项目需要进行功能测试、性能测试和接口测试,顿时压力倍增(于是我把压力(鸭梨)放在了冰箱里,就变成了动力(冻梨)),此前对性能测试一无所知,了解程度只能用"听过"来形容。性能测试首选的工具是JMeter,在此不多做介绍,但是不得不说JMeter也是一款非常好的接口测试工具。性能测试过程中手工重复的活动非常多,为了给客户提供一个性能测试报告,我用了一周时间进行并发测试、数据整理、数据分析、最后生成测试报告,真的是手工重复到怀疑人生;于是萌生了实现性能测试自动化的想法。之前用Robot framework框架做过WE...
            0 4 2350
            分享
          • 在Python开发中,数据存储、读取是必不可少的环节,而且可以采用的存储方式也很多,常用的方法有json文件、csv文件、MySQL数据库、Redis数据库以及Mongdb数据库等。1. json文件存储数据json是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据,可以轻松解决py2和py3的编码问题,内容结构类似于python中的字典和列表,层次结构简洁而清晰,易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。2. csv文件Python可以将数据存储为CSV文件格式,我们可以用excel打开CSV文档,进行数据的浏览,十分方便,以下是将数据存...
            13 13 1327
            分享
          •   全球各地的企业每天都在发展变化着,以应对市场挑战,满足日益成熟的客户需求。即使是正在进行的技术进步也会使软件测试专家在实践的过程中更加专注和精确。  2021年给软件测试领域带来了新的技术解决方案,以及质量保证和软件测试的实现。与此同时,诸如敏捷、DevOps、DevSecOps和测试自动化等实践继续在整个软件测试周期中保持其相关性和应用。  而2022年软件测试和开发领域的一些强劲趋势主要有以下几个:  1、人工智能促进软件测试  《福布斯》的一篇题为《软件测试中的人工智能:机器人会代替你的位置吗?》中提到:“依赖技术完成高重复性任务的趋势,同时使人们能够专注于高价值的活动,如创造收入、...
            0 1 1770
            分享
          • 可用性测试可用性测试是从用户的角度测试应用程序,以检查外观和用户友好性。例如,有一个用于股票交易的移动应用程序,并且测试人员正在执行可用性测试。测试人员可以检查该移动应用程序是否易于单手操作,滚动条应垂直,应用程序背景颜色应为黑色,价格和股票以红色或绿色显示的场景。此类应用程序可用性测试的主要思想是,用户一打开应用程序,用户就应该对市场有所了解。a) 探索性测试探索性测试是由测试团队执行的非正式测试。此测试的目的是探索应用程序并查找应用程序中存在的缺陷。测试人员使用业务领域的知识来测试应用程序。测试章程用于指导探索性测试。b) 跨浏览器测试跨浏览器测试是在不同的浏览器、操作系统、移动设备上测试...
            0 0 928
            分享
      • 51testing软件测试圈微信