客户端自动化测试小探索

本文背景:最近花了两三天时间小探索了一下客户端的自动化测试,起因是因为公司项目在提测之前必须自己手点一些固有的测试case,确定没问题才能提测。实在懒得点,就想探寻一下有没有比较简单的办法解决这个问题。

技术选型

客户端测试我了解的不是很专业,在我的之前的认知里,主要由appium,macaca等构成,都是通过脚本去控制UI的各种操作,抓取元素模拟点击等等。适用场景上,确实比较适合做一些固有case的回归,但是脚本维护的成本略高,不过支持的语言够多,尤其是appium,几乎支持了所有主流语言,我试着写了一下,问题不大,就是当固有case发生变化的时候比较惨,代码很多都没法用了。虽然理论上讲固有case是不会发生变化的,但是在业务导向的团队中,不可能百分百保证固有case不发生变化,尤其还是在一个新业务的探索发展阶段。

我是一个Android DEVER,那么我想了一个够简单的方法:通过AS2.2版本的新特性:Espresso的单元测试录制,让写UT更轻松。但是在我试过之后发现一个鸡肋的问题:录制好的脚本不更改几乎没法用。因为在大型的app上,元素的定位不是那么简单的,而录制的脚本中几乎都用allOf来定位元素,显然就是不行的,那么维护脚本的成本就大了,很多情况下不亚于直接用appium直接去写,所以这个方案我就放弃掉了。

我理想中的方案是:维护脚本成本够低、写test case够简单、可读性够强、如果能Android & iOS共用那更棒,最关键的一点:时间成本要少

于是我邂逅了Calabash

Calabash上手

我第一眼就相中它的原因就是:它可以用一种类似自然语言的方式编写测试用例。以往的方式都是TDD,而calabash是BDD,这让我感到很新奇,于是我做了一波尝试。

先给大家看看我写的第一个固有case(是固有case里最简单的一个):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Feature: xxx固有用例
Scenario: 功能:首页
Then I see "跳过"
When I press "跳过"
Then I press "上海"
When I press view with id "city"
Then I see "海外"
When I press "海外"
And I press view with id "start_search"
When I enter "东京" into input field number 1
Then I press list item number 1
Then I see "东京"
When I press "美食"
Then I see "美食"
Then I go back
When I see "景点"
Then I press "景点"
Then I see "景点"
Then I go back
Then I press view with id "title_image"
Then I wait for 5 seconds
Then I press webview text "特价机票"
Then I wait for 5 seconds
Then I go back
Then I go back
When I scroll down recyclerview with id "main_listview" count 5
Then I see "猜你喜欢"
Then I press view with id "root_rl" number 1
Then I wait for 10 seconds

这个没写过代码的人我相信都看得懂,其实就是用我们常规的思维模式去写测试用例。用法非常简单,如果是Mac的小伙伴那简直不要太爽,天生自带ruby,直接用gem去安装calabash就可以,这里推荐安装calabash-android,其实安装calabash-ios应该是类似的,大家去calabash的github organization中找相应平台的repo就可以,这里我以Android为例。

安装完之后,直接用calabash gen生成目录就可以,固有的目录都会自动生成,其实主要和我们相关的就是features目录。
这里面分为三部分:

  • step宏定义+自定义
  • 各种hooks的support支持
  • 自己写的测试用例,文件名用.feature结尾

很简单,上面的测试case就是自己写的测试用例。到这里我突然要告诉大家:calabash只支持ruby。其实如果我不说,估计大家都考虑不到语言,因为从这个case上看,根本看不出语言。不过大家别担心,这里用到ruby的地方无非在一些自定义step和hooks,其他的目前我没发现哪里还需要用了,而且用到的ruby极其基础简单,不需要学,不知道的Google一下就完事了。

最后用calabash run命令启动就可以,别忘了要有apk或者ipa包,这部分我就不细说了,去github看会比较详细。

其实到这里就讲完了,接下来我会分享一下我这两天遇到的坑,也是我觉得用calabash的难点。

疑难杂症

难点一:定位元素

这部分以Android为例,显然很多页面经常会出现id相同的情况,那么我们要通过各种手段定位到唯一的元素。Android极其推荐使用sdk tools目录下的uiautomatorviewer进行元素抓取和定位,配合肉眼和calabash console进行定位,三者结合基本上差不多,无非就是花时间多少的问题。那么webview部分calabash也是支持的,主要是通过css以及各种条件筛选进行定位,其中text内容匹配用到的最多。

这部分只能慢慢摸索,有经验就会快了。

在定位元素的过程中,会遇到一些宏定义的step不够用的情况,需要自己自定义step。这里给大家看我这两天自定义的几个step:

1
2
3
4
5
6
7
8
9
10
11
12
13
Then /^I press webview text "([^\"]*)"$/ do |text|
touch("android.webkit.WebView css:'a' {textContent CONTAINS[c] '#{text}'}")
end
Then /^I press view with id "([^\"]*)" number (\d+)$/ do |id, number|
tap_when_element_exists("* id:'#{id}' index:#{number}")
end
When /^I scroll down recyclerview with id "([^\"]*)" count (\d+)$/ do |id, count|
for i in 0..Integer(count)
scroll("android.support.v7.widget.RecyclerView id:'#{id}'", :down)
end
end

这ruby很简单把,无非就是包了一层calabash的api然后识别了一下,没啥技术含量,主要就是自己方便用,扩展一下step,这三个自定义的step我在上面的case中都有用到。

难点二:流程控制

Feature

我们在上面的case中可以看到有Feature的标识,这是calabash的一个控制单元(我的理解),每运行一个Feature,apk都会卸载重装,保证每次都是全新无副作用的应用。

Scenario

这个标识是囊括在Feature中的,一个Feature可以有很多个Scenario,运行每个Scenario的时候应用只会退出再重进,不会卸载再重装。

Hooks

在feature/support包下,我们会看到这么几个ruby文件:

  • app_installation_hooks.rb
  • app_life_cycle_hooks.rb
  • env.rb
  • hooks.rb

可以看到,基本上都是hook用的,我们看一个简单的app_life_cycle_hooks.rb理解理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
require 'calabash-android/management/adb'
require 'calabash-android/operations'
Before do |scenario|
start_test_server_in_background
end
After do |scenario|
if scenario.failed?
screenshot_embed
end
shutdown_test_server
end

这是默认就生成好的,从字面意思上看,就是app生命周期的hook,这里可以看到,在主要用到了BeforeAfter关键字进行操作,这个显然很容易就看懂了,于是我自己写了一个在每个step执行之后都等待2秒的hook,下面的代码写在hooks.rb中(这个文件默认生成是空的):

1
2
3
4
5
require 'calabash-android/calabash_steps'
AfterStep do |scenario|
sleep 2
end

很容易对吧?关于这部分想多了解的可以看cucumber wiki中的Hooks部分

总结

首先,calabash是我最后唯一选中的客户端自动化固有case的方案。这里说下原因:

  • 脚本维护成本很低,就算固有case发生变化,也可以花很少的时间迅速更改脚本
  • 我们的业务固有case数量不大,操作主要涵盖的部分calabash都可以完成,编写脚本的成本低,无语言障碍
  • 可读性非常强,代码简单易懂,任何一位DEVER都可以零成本上手,有Mac的连装环境都差不多省了
  • 小范围组内使用成本低,坑有但是并不大,都可以解决,某些机型不兼容问题直接用genymotion跑就行了,最简单(主要还是固有case不复杂)
  • Android & iOS都可以使用
  • 玩起来很有意思,对于我这种懒人来说是福音

估计还有一些高级的玩法我还不会,不过目前这些都够我写一些固有case了,目前我们组内将会对这个方案进行评估,没啥问题就组内小范围用了,如果有感兴趣的朋友欢迎一起讨论唷。

谢谢大家。

RichardCao wechat
欢迎关注我的个人微信公众号,不定期推送精彩内容~
坚持原创技术分享,您的支持将鼓励我继续创作!