Appium 从 0 到 1 搭建移动 App 功能自动化测试平台 (3):编写 iOS 自动化测试脚本

debugtalk · 2016年05月30日 · 最后由 jorson 回复于 2016年07月20日 · 8177 次阅读
本帖已被设为精华帖!

通过前面三篇文章,我们已经将 iOS 自动化功能测试的开发环境全部准备就绪,也学习了 iOS UI 控件交互操作的一般性方法,接下来,就可以开始编写自动化测试脚本了。

在本文中,我将在 M 项目中挑选一个功能点,对其编写自动化测试脚本,演示编写自动化测试用例的整个流程。

语言的选择:Python or Ruby?

之前介绍 Appium 的时候也提到,Appium 采用 Client-Server 的架构设计,并采用标准的 HTTP 通信协议;Client 端基本上可以采用任意主流编程语言编写测试用例,包括但不限于 C#、Ruby、Objective-C、Java、node.js、Python、PHP。

因此,在开始编写自动化测试脚本之前,首先需要选定一门编程语言。

这个选择因人而异,并不涉及到太大的优劣之分,基本上在上述几门语言中选择自己最熟悉的就好。

但对我而言,选择却没有那么干脆,前段时间在 Python 和 Ruby 之间犹豫了很久,经过艰难的决定,最终选择了 Ruby。为什么不考虑 Java?不熟是一方面,另一方面是觉得采用编译型语言写测试用例总感觉太重,这活儿还是解释型语言来做更合适些。

其实,最开始本来是想选择 Python 的,因为 Python 在软件测试领域比 Ruby 应用得更广,至少在国内,不管是公司团队,还是测试人员群体,使用 Python 的会比使用 Ruby 的多很多。

那为什么还是选择了 Ruby 呢?

我主要是基于如下几点考虑的:

  • 从 Appium 的官方文档来看,Appium 对 Ruby 的支持力度,或者说是偏爱程度,貌似会更大些;在Appium Client Libraries列表中将 Ruby 排在第一位就不说了,在Appium Tutorials中示例语言就只采用了 Ruby 和 Java 进行描述。
  • Appium_Console是采用 Ruby 编写的,在 Console 中执行的命令基本上可直接用在 Ruby 脚本中。
  • 后续打算引入 BDD(行为驱动开发)的测试模式,而不管是 cucumber 还是 RSpec,都是采用 Ruby 开发的。

当然,还有最最重要的一点,身处于珠江三角洲最大的 Ruby 阵营,周围 Ruby 大牛云集,公司的好多业务系统也都是采用 Rails 作为后台语言,完全没理由不选择 Ruby 啊(测试用例还可以拉人一起写啊_)。

第一个测试用例:系统登录

在测试领域中,系统登录这个功能点的地位,堪比软件开发中的Hello World,因此第一个测试用例就毫无悬念地选择系统登录了。

在编写自动化测试脚本之前,我们首先需要清楚用例执行的路径,路径中操作涉及到的控件,以及被操作控件的属性信息。

对于本次演示的 APP 来说,登录时需要先进入【My Account】页面,然后点击【Login】进入登录页面,接着在登录页面中输入账号密码后再点击【Login】按钮,完成登录操作。

确定了操作路径以后,就可以在Appium Ruby Console中依次操作一遍,目的是确保代码能正确地对控件进行操作。

第一步要点击【My Account】按钮,因此先查看下 Button 控件属性。要是不确定目标控件的类型,可以直接执行page命令,然后在返回结果中根据控件名称进行查找。

[1] pry(main)> page :button
...(略)
UIAButton
   name, label: My Account
   id: My Account => My Account
nil

通过返回结果,可以看到【My Account】按钮的 name、label 属性就是 “My Account”,因此可以通过button_exact('My Account')方式来定位控件,并进行点击操作。

[2] pry(main)> button_exact('My Account').click
nil

执行命令后,观察 iOS 模拟器中 APP 的响应情况,看是否成功进入 “My Account” 页面。

第二步也是类似的,操作代码如下:

[3] pry(main)> button_exact('Login').click
nil

进入到登录页面后,再次查看页面中的控件信息:

[4] pry(main)> page
...(略)
UIATextField
   value: Email Address
   id: Email Address => Email Address
UIASecureTextField
   value: Password (6-16 characters)
   id: Password (6-16 characters) => Password (6-16 characters)
UIAButton
   name, label: Login
   id: Log In => Login
       登录     => Login
...(略)

第三步需要填写账号密码,账号密码的控件属性分别是UIATextFieldUIASecureTextField。由于这两个控件的类型在登录页面都是唯一的,因此可以采用控件的类型来进行定位,然后进行输入操作,代码如下:

[5] pry(main)> tag('UIATextField').type 'leo.lee@dji.com'
""
[6] pry(main)> tag('UIASecureTextField').type '123456'
""

执行完输入命令后,在 iOS 模拟器中可以看到账号密码输入框都成功输入了内容。

最后第四步点击【Login】按钮,操作上和第二步完全一致。

[7] pry(main)> button_exact('Login').click
nil

执行完以上四个步骤后,在 iOS 模拟器中看到成功完成账号登录操作,这说明我们的执行命令没有问题,可以用于编写自动化测试代码。整合起来,测试脚本就是下面这样。

button_exact('My Account').click
button_exact('Login').click
tag('UIATextField').type 'leo.lee@dji.com'
tag('UIASecureTextField').type '12345678'
button_exact('Login').click

将以上脚本保存为login.rb文件。

但当我们直接运行login.rb文件时,并不能运行成功。原因很简单,脚本中的button_exacttag这些方法并没有定义,我们在文件中也没有引入相关库文件。

在上一篇文章中有介绍过,通过arc启动虚拟机时,会从appium.txt中读取虚拟机的配置信息。类似的,我们在脚本中执行自动化测试时,也会加载虚拟机,因此同样需要在脚本中指定虚拟机的配置信息,并初始化Appium Driver的实例。

初始化代码可以通过Appium Inspector生成,基本上为固定模式,我们暂时不用深究。

添加初始化部分的代码后,测试脚本如下所示。

require 'rubygems'
require 'appium_lib'

capabilities = {
    'appium-version' => '1.0',
    'platformName' => 'iOS',
    'platformVersion' => '9.3',
}
server_url = "http://0.0.0.0:4723/wd/hub"
Appium::Driver.new(caps: capabilities).start_driver
Appium.promote_appium_methods Object

# testcase: login
button_exact('My Account').click
button_exact('Login').click
tag('UIATextField').type 'leo.lee@dji.com'
tag('UIASecureTextField').type '123456'
button_exact('Login').click

driver_quit

优化测试脚本:加入等待机制

如上测试脚本编写好后,在 Terminal 中运行ruby login.rb,就可以执行脚本了。

运行命令后,会看到 iOS 虚拟机成功启动,接着 App 成功进行加载,然后自动按照前面设计的路径,执行系统登录流程。

但是,在实际操作过程中,发现有时候运行脚本时会出现找不到控件的异常,异常信息如下所示:

➜ ruby login.rb
/Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/common/helper.rb:218:in `_no_such_element': An element could not be located on the page using the given search parameters. (Selenium::WebDriver::Error::NoSuchElementError)
    from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/ios/helper.rb:578:in `ele_by_json'
    from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/ios/helper.rb:367:in `ele_by_json_visible_exact'
    from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/ios/element/button.rb:41:in `button_exact'
    from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/driver.rb:226:in `rescue in block (4 levels) in promote_appium_methods'
    from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/driver.rb:217:in `block (4 levels) in promote_appium_methods'
    from login.rb:28:in `<main>'

更奇怪的是,这个异常并不是稳定出现的,有时候能正常运行整个用例,但有时在某个步骤就会抛出找不到控件的异常。这是什么原因呢?为什么在Appium Ruby Console中单步操作时就不会出现这个问题,但是在执行脚本的时候就会偶尔出现异常呢?

原来,在我们之前的脚本中,两条命令之间并没有间隔时间,有可能前一条命令执行完后,模拟器中的应用还没有完成下一个页面的加载,下一条命令就又开始查找控件,然后由于找不到控件就抛出异常了。

这也是为什么我们在Appium Ruby Console中没有出现这样的问题。因为手工输入命令多少会有一些耗时,输入两条命令的间隔时间足够虚拟机中的 APP 完成下一页面的加载了。

那针对这种情况,我们要怎么修改测试脚本呢?难道要在每一行代码之间都添加休眠(sleep)函数么?

也不用这么麻烦,针对这类情况,ruby_lib实现了wait机制。将执行命令放入到wait{}中后,执行脚本时就会等待该命令执行完成后再去执行下一条命令。当然,等待也不是无休止的,如果等待 30 秒后还是没有执行完,仍然会抛出异常。

登录流程的测试脚本修改后如下所示(已省略初始化部分的代码):

wait { button_exact('My Account').click }
wait { button_exact('Login').click }
wait { tag('UIATextField').type 'leo.lee@dji.com' }
wait { tag('UIASecureTextField').type '123456' }
wait { button_exact('Login').click }

对脚本添加wait机制后,之前出现的找不到控件的异常就不再出现了。

优化测试脚本:加入结果检测机制

然而,现在脚本仍然不够完善。

我们在Appium Ruby Console中手工执行命令后,都是由人工肉眼确认虚拟机中 APP 是否成功进入下一个页面,或者返回结果是否正确。

但是在执行自动化测试脚本时,我们不可能一直去盯着模拟器。因此,我们还需要在脚本中加入结果检测机制,通过脚本实现结果正确性的检测。

具体怎么做呢?

原理也很简单,只需要在下一个页面中,寻找一个在前一个页面中没有的控件。

例如,由 A 页面跳转至 B 页面,在 B 页面中会存在 “Welcome” 的文本控件,但是在 A 页面中是没有这个 “Welcome” 文本控件的;那么,我们就可以在脚本中的跳转页面语句之后,加入一条检测 “Welcome” 文本控件的语句;后续在执行测试脚本的时候,如果页面跳转失败,就会因为找不到控件而抛出异常,我们也能通过这个异常知道测试执行失败了。

当然,对下一页面中的控件进行检测时同样需要加入等待机制的。

登录流程的测试脚本修改后如下所示(已省略初始化部分的代码):

wait { button_exact('My Account').click }
wait { text_exact 'System Settings' }

wait { button_exact('Login').click }
wait { button_exact 'Forget password?' }

wait { tag('UIATextField').type 'leo.lee@dji.com' }
wait { tag('UIASecureTextField').type '12345678' }
wait { button_exact('Login').click }
wait { text_exact 'My Message' }

至此,系统登录流程的自动化测试脚本我们就编写完成了。

To be continued ...

在本文中,我们通过系统登录这一典型功能点,演示了编写自动化测试用例的整个流程。

在下一篇文章中,我们还会对自动化测试脚本的结构进行进一步优化,并实现测试代码工程化。


Read More ...

公众号:DebugTalk
原文链接:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-write-iOS-testcase-scripts

相关文章

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 23 条回复 时间 点赞

给个建议,眼光可以放远点,放宽点,才决定用什么语言好,ui 自动化只是一个工具

—— 来自 TesterHome 官方 安卓客户端

#1 楼 @taki Ruby 也没啥不好吧

#2 楼 @debugtalk 没说不好,比如安卓的东西客户端,服务端都是 java 开发,包括各种安卓的工具工具,性能,安全,几乎服务端都是 java,就是面广一些几乎都是 java 服务,所以用 java 后续还是有点优势

—— 来自 TesterHome 官方 安卓客户端

没必要拘泥于哪种语言,自己喜欢就好,能带来满足感

楼主,咨询你一个问题,在安装 Appium Ruby Console 的时候我遇到了这样的问题,日志如下

SvenWeng@wengyanbindeMBP:~|⇒  sudo gem install --no-rdoc --no-ri appium_console bond
Password:
Building native extensions.  This could take a while...
/Library/Ruby/Site/2.0.0/rubygems/ext/builder.rb:76: warning: Insecure world writable dir /usr/local/bin in PATH, mode 040777
ERROR:  Error installing appium_console:
    ERROR: Failed to build gem native extension.

    current directory: /Library/Ruby/Gems/2.0.0/gems/nokogiri-1.6.7.2/ext/nokogiri
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -r ./siteconf20160531-21826-12du3tr.rb extconf.rb
checking if the C compiler accepts ... yes
checking if the C compiler accepts -Wno-error=unused-command-line-argument-hard-error-in-future... no
Building nokogiri using packaged libraries.
Using mini_portile version 2.0.0
checking for iconv.h... yes
checking for gzdopen() in -lz... yes
checking for iconv... yes

Team Nokogiri will keep on doing their best to provide security
updates in a timely manner, but if this is a concern for you and want
to use the system library instead; abort this installation process and
reinstall nokogiri as follows:

    gem install nokogiri -- --use-system-libraries
        [--with-xml2-config=/path/to/xml2-config]
        [--with-xslt-config=/path/to/xslt-config]

看日志是说没有安装 nokogiri,于是我就安装 nokogiri,报了这个错:

1 warning generated.
ld: warning: ignoring file /usr/local/lib/liblzma.dylib, file was built for x86_64 which is not the architecture being linked (i386): /usr/local/lib/liblzma.dylib
checked program was:
/* begin */
 1: #include "ruby.h"
 2: 
 3: /*top*/
 4: extern int t(void);
 5: int t(void) { main(); return 0; }
 6: int main(int argc, char **argv)
 7: {
 8:   if (argc > 1000000) {
 9:     printf("%p", &t);
10:   }
11: 
12:   return 0;
13: }
/* end */

--------------------

have_func: checking for xmlParseDoc() in libxml/parser.h... -------------------- no

Google 告诉我是 libxml2 libxslt 这两个东西没装,于是我运行了 brew install libxml2 libxslt

SvenWeng@wengyanbindeMBP:~|⇒  brew install libxml2 libxslt
Warning: libxml2-2.9.3 already installed
Warning: libxslt-1.1.28_1 already installed

所以我最终还是没有装上 Appium Ruby Console

我的电脑是 Mac OS X EI Capitan 10.11.5

求帮忙支招

选语言,我感觉应该考虑下维护的成本,直白点说就是你能不能找到第二个人跟你协作,还有你走了后,这摊东西找不找得到人来接手。

#5 楼 @wyb199026 这些问题我没有遇到过,估计是之前有安装过一些包,冲突了。

按照官方文档试下吧:

gem update --system ;\
gem update bundler
gem uninstall -aIx appium_lib ;\
gem uninstall -aIx appium_console ;\
gem install --no-rdoc --no-ri appium_console bond

另外,如果采用系统的 Ruby 环境总是有问题,还是尝试用 RVM 单独搞个环境吧。

#3 楼 @taki 如果是只搞 Android 的话,选择 Java 和 Python 的确会更好些;但是现在不只局限于 Android,还有 iOS,以及后台 Rails,所以综合考虑,感觉 Ruby 更合适些

#4 楼 @alfredhu 是的,不必拿着一把锤子看啥都是钉子 _^

#6 楼 @jmcn 当前公司里面写 Ruby 的非常多,所以也是考虑到这点

#7 楼 @debugtalk 恩,我试试搞一个 RVM,我是按照官方的方式来的,安装最后一步的时候遇到的问题。

楼主想问下,真机测试时是不是不能够自动安装 app,只能手动安装到手机上?

#12 楼 @zky_wind 可以安装到真机上,不过安装包不是 .app,而是经过签名的 .ipa

#13 楼 @debugtalk 自动安装到真机上是不是就是把签过名的.ipa 的路径放到这里面 capabilities.setCapability("app", “............”);就行了?为什么安装还是报错,帮忙讲解下,谢谢

请问楼主,为什么我的 ios 环境下的 appium 写的 case 运行非常慢,简直慢到无法容忍

匿名 #16 · 2016年06月22日

你好,您的帖子中说 “初始化代码可以通过 Appium Inspector 生成”,请问怎么生成啊?我准备用 python 写脚本。

#16 楼 @wowotou 在上一篇文章中写的,链接

#15 楼 @xhlsyyyzyq 当前我在模拟器中运行的,也是挺慢的,还在摸索

#18 楼 @debugtalk 貌似是查找元素的过程很慢,特别是页面布局复杂的时候。找一个控件可能会花上 1-2 分钟。有这么慢吗

ios 环境下,写的 case,在模拟器和真机上运行慢死了都,你们都有什么好的解决方法吗?

选 Python

—— 来自 TesterHome 官方 安卓客户端

新手学习

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册