Android终端单测杂烩

2019-07-12   出处:腾讯移动品质中心TMQ  作/译者:王非凡  
  • 给测试同学-关于语言补习

 Kotlin

*建议Java全熟之后再看,同时看有可能会记错用法;

*语法比较多,需要慢慢消化;

*优先看下官网的Higher-Order Functions and Lambdas还有inline functions有助于尽快看懂开发代码。

 Java

*泛型需要优先看下,其它结合开发代码学习。

 以上两种,先看Java再跟进Kotlin的话,体感大概一~二周差不多可以读懂开发代码+写一些单测用例。有相关经验会更快一些。


  • 给测试同学-Gradle

实际开始投入单测之后发现有不少坑都在Gradle里面,所以需要大致了解Gradle,磨好刀再砍柴。基础资料搜索一下网络还是比较全的。

下面是两个刚开始接触时遇到的问题:

A.默认的项目test文件里面用的都是Android.support.test.....,因为建议用Androidx代替所以用例文件引用的都是Androidx,结果运行用例的时候会这样报错:

这里需要把Gradle里面的testInstrumentationRunner也替换到androidx.test.runner.AndroidJUnitRunner。

B.dependencies结构:

依赖的一些外部的包在这里配置,其中testimplementation和Androidtestimplementation分别作用于工程路径src/test和src/androidTest

如果没用implementation而是compileOnly则表示只编译,不打包。整体编译情况下这么操作是ok的,但是单测场景下测试单个模块时就可能导致找不到实现。为了不影响最后出包,可以添加对应模块的testimplementation和Androidtestimplementation代替。


  • 单测中获取context

Instrument test里面经常要获取context,对于单测来说可以直接使用InstrumentationRegistry.getInstrumentation.context获取,需要注意对应的metadata/versioncode等等数据全部与当前模块路径下的Androidtest/test文件夹内的对应文件挂钩,而不是和工程的APP文件夹挂钩,其中:

InstrumentationRegistry.getInstrumentation()

返回当前正在运行的Instrumentation;      

InstrumentationRegistry.getContext()

返回此Instrumentation软件包的上下文;

InstrumentationRegistry.getTargetContext()

返回目标应用的应用上下文;

InstrumentationRegistry.getArguments()

返回传递给此Instrumentation的参数Bundle。


  • Manifest—runtime permission—rule—@get—api23

API23(Android6.0)之后,申请权限变成了在运行时获取(用户点了某个功能之后APP弹框提示用户是否授权etc),单测时则需要用androidx.test.rule.GrantPermissionRule.grant(Manifest.permission.STRING)获取对应的权限(android.support.test参考开头)。或者先弄出一个Activity然后动态申请权限(情况比较少,而且很麻烦)。

其中,java可以用

@Rule 

public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)实现一次性申请权限;但是如果直接写到kotlin里面会报错:

java.lang.Exception: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded. Check your build configuration。所以在kotlin里面需要改写成:@get:Rule val mRuntimePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)

 

  • AndroidX、Android.support有什么区别?

在上一节引用GrantPermissionRule时发现Developer上给了两个不同包下的同名东东:

而本地看到的工程里面大多数引用的是Androidx.test,为防止重复引入导致踩坑,查了一下,大概解释如下:

也就是说androidx是android.support的强化升级版。后面一般有要引入的包,优先在androidx里面查找就好。

P.S.:如果遇到兼容旧版本使用了Android.support的程序,可能还是要配合用回android.support。

 

  • Mockk—static method—AndroidP

mockK声称支持静态方法的mock,然后试验了一下在荣耀V9的表现:

所以想mock静态方法的,找找AndroidP的手机吧。 

另外mock静态方法的代码(kotlin):

后面的every语句直接用class.method填写即可。

 

  • Mock private method

Mock一个私有方法时,使用以下语句:

    Mock[“methodname”]()

而如果想要把私有方法加到verify跟踪内,在mock的时候要加上(recordPrivateCalls=true):

            class Car {    

            fun drive() = accelerate()    

            private fun accelerate() = "going faster"

            }

            val mock = spyk<Car>( recordPrivateCalls = true)

            every { mock["accelerate"]() } returns "going not so fast"

此外如果想Mock私有属性,建议采用反射替代mock。


  • No implementation found—ndk

最初报错出自于申请了io权限之后,然后手机就一直报错找不到implementation of xxx,debug打印的错误提示如下:

然后在全局搜索下找到了这个丢了的文件,在build里面一个很深的文件夹,不过文件夹名称是armeabi-v7a,而运行时候却非要在arm64-v8a里面找,找了一圈也没发现在哪能设置这个搜索路径。最后问了一下,这里的查找方式是优先找v8,如果有这个文件夹就不在其它文件夹找了,而现在这个文件夹里面又没有libqgfilelog-lib.so(因为不支持64位),结果提示找不到。解决方案是在gradle里面添加ndk{abiFilters “armeabi-v7a”},就可以跳过v8文件夹只查找v7a的文件夹了。

至于为什么多出来了个arm64文件夹,最后推测可能是本地之前新建过一个对应的模拟器。不过为什么运行GrantPermissionRule.grant()之后会触发这条路径搜索,暂时还没搞清楚。

  

  • 环境配置

IDE本身没有太多需要配置的,代理配置好就ok

 

  • 如何Mock 无返回值的方法:

暂时没找到和mockito一样的处理(mockito有专门对应void返回类型的几条语句),下面是mockk网站上一个类似的方案

Mocking nothing

Nothing special here. If you have a function returning Nothing:

Then you need to throw some exception as a behaviour:

  • 覆盖率工具

a) Local unit test

如果单测用例是本地用例,可以直接使用AS自带的工具。首先进入run/debug配置页面,新增一条JUnit配置,选项分别填写:

Test kind: All in Package

Package:测试代码所在的package

Search for tests:Across Module Dependencies

VM-options:-ea(默认就是这个_)

Working directory:工程路径

Use ClassPath of mod:选择待测模块

配置后保存,然后在工具栏选择debug右侧的“Run xxx with Coverage”(各个版本AS展示的图标不太一样),等待跑完就可以看到coverage窗口给出覆盖率

b) Instrumented test

需要使用jacoco跟踪生成覆盖率报表

首先在需要生成数据的模块Gradle内添加buildTypes{debug{testCoverageEnabled true}}

重新同步后,命令行依次执行:

./gradlew testDebugUnitTest

./gradlew createDebugCoverageReport

批跑结束后在对应模块的build/reports/coverage/debug/index.html内可以看到详细的覆盖率数据:

最后抛一个问题:大家觉得单测中是否有必要控制mock的使用(能mock的地方全部使用mock  VS 只有mock才能解决的时候才使用mock)?


欢迎给测试窝投稿或参与内容翻译工作,请邮件至editors@testwo.com。也欢迎大家通过新浪微博(@测试窝)或微信公众号(测试窝)关注我们,并与我们的编辑和其他窝友交流。
129°|1297 人阅读|0 条评论

登录 后发表评论