从 0 到 1 搭建移动 App 功能自动化测试平台 (4):自动化测试代码⎡工程化⎦

在本系列的上一篇文章中,我通过系统登录这一典型功能点,演示了编写自动化测试脚本的整个流程,并对测试脚本进行了初步优化。

在本文中,我将重点介绍如何对自动化测试脚本实现⎡工程化⎦的组织和管理。

测试脚本⎡工程化⎦

首先说下什么是测试脚本的工程化。

通过之前的工作,我们已经可以让单个自动化测试用例正常运行起来了。然而,这还只算是一个demo,一切才刚刚开始。

试想,一个项目的自动化测试用例少则数百,多则成千上万。如何将这些自动化测试用例组织起来?如何实现更好的可重用机制?如何实现更好的可拓展机制?这些都还是我们当前的demo所不具备的,也是我们需要通过“工程化”手段进行改造的原因。

引入Minitest/RSpec

在Ruby中,说到测试首先就会想到Minitest或RSpec,这是Ruby中用的最多的两个测试框架。通过这些框架,我们可以很好地实现对Ruby测试用例的管理。

同样地,由于我们的自动化测试脚本是采用Ruby编写的,因此我们也可以使用Minitest/RSpec来管理我们的自动化测试用例。

基于该想法,我们采用RSpec对之前的系统登录测试用例进行工程结构初始化。对于熟悉Ruby编程,或者有一定代码基础的同学而言,很自然地,可以将测试用例框架初始化为如下结构。

├── Gemfile
├── android
│   └── appium.txt
├── common
│   ├── requires.rb
│   └── spec_helper.rb
└── ios
    ├── appium.txt
    └── spec
        └── login_spec.rb

Gemfile中,指定了项目依赖的库。

# filename: Gemfile
source 'https://gems.ruby-china.org'

gem 'rspec'
gem 'appium_lib'
gem 'appium_console'

common/spec_helper.rb中,定义了模拟器和RSpec初始化相关的代码。

# filename: common/spec_helper.rb

def setup_driver
  return if $driver
  appium_txt = File.join(Dir.pwd, 'ios', 'appium.txt')
  caps = Appium.load_appium_txt file: appium_txt
  Appium::Driver.new caps
end

def promote_methods
  Appium.promote_appium_methods RSpec::Core::ExampleGroup
end

setup_driver
promote_methods

RSpec.configure do |config|

  config.before(:each) do
    $driver.start_driver
    wait { alert_accept }
  end

  config.after(:each) do
    driver_quit
  end

end

common/requires.rb中,实现了对相关库文件的引用。

# filename: common/requires.rb

# load lib
require 'rspec'
require 'appium_lib'

# setup rspec
require_relative 'spec_helper'

ios/appium.txt中,对iOS模拟器信息和测试包路径进行了配置。

[caps]
platformName = "ios"
deviceName = "iPhone 6s"
platformVersion = "9.3"
app = "/Users/Leo/MyProjects/AppiumBooster/ios/app/test.app"

ios/spec/目录中,则是测试用例的内容。例如,ios/spec/login_spec.rb对应的就是系统登录的测试用例。

# filename: ios/spec/login_spec.rb
require_relative '../../common/requires'

describe 'Login' do

  it 'with valid account' do
    wait { id('btnMenuMyAccount').click }
    wait { id 'uiviewMyAccount' }

    wait { id('tablecellMyAccountLogin').click }
    wait { id 'uiviewLogIn' }

    wait { id('txtfieldEmailAddress').type 'leo.lee@dji.com' }
    wait { id('sectxtfieldPassword').type '123321' }
    wait { id('btnLogin').click }
    wait { id 'tablecellMyMessage' }
  end

end

通过以上代码结构初始化,我们的测试用例框架的雏形就形成了。接下来,在Terminal中切换到项目根目录,然后通过rspec ios命令就可以执行ios目录中的测试用例了。

➜ rspec ios
.

Finished in 2 minutes 7.2 seconds (files took 1.76 seconds to load)
1 example, 0 failures

完整的代码请参考debugtalk/AppiumBooster1.FirstTest分支

添加第二条测试用例

现在,我们尝试往当前的测试框架中添加第二条测试用例。

例如,第二条测试用例要实现启动后从当前地区切换至中国。那么,就可以新增ios/spec/change_country_spec.rb

# filename: ios/spec/change_country_spec.rb
require_relative '../../common/requires'

describe 'Change country' do

  it 'from Hong Kong to China' do
    wait { id('btnMenuMyAccount').click }
    wait { id 'uiviewMyAccount' }

    wait { id('tablecellMyAccountSystemSettings').click }
    wait { id 'txtCountryDistrict' }

    wait { id('txtCountryDistrict').click }
    wait { id 'uiviewSelectCountry' }

    wait { id('tablecellSelectCN').click }

    wait { id('btnArrowLeft').click }
    wait { id 'uiviewMyAccount' }
  end

end

完整的代码请参考debugtalk/AppiumBooster2.SecondTest分支

现在我们凝视已经添加的两个测试用例,有发现什么问题么?

是的,重复代码太多。在每一步操作中,都要用id来定位控件,还要用wait来实现等待机制。

除此之外,当前代码最大的问题就是测试用例与控件映射杂糅在一起。造成的后果就是,不管是控件映射发生变动,还是测试用例需要修改,都要来修改这一份代码,维护难度较大。

重构:测试用例与控件映射分离

基于以上问题,我们首要的改造任务就是将测试用例与控件映射进行分离。

考虑到常用的控件操作方法就只有几个(clicktype),因此我们可以将控件操作方法单独封装为一个模块,作为公共模块。

module Actions

  def click
    wait { @found_cell.click }
  end

  def type(text)
    wait { @found_cell.type text }
  end

end

然后,将APP中每一个页面封装为一个模块(module),将页面中的控件映射为模块的静态方法(method),并通过include机制引入方法模块。

例如,登录页面就可以封装为如下代码。

module Pages
  module Login
    class << self

      include Actions

      def field_Email_Address
        @found_cell = wait { id 'txtfieldEmailAddress' }
        self
      end

      def field_Password
        @found_cell = wait { id 'sectxtfieldPassword' }
        self
      end

      def button_Login
        @found_cell = wait { id 'btnLogin' }
        self
      end

    end
  end
end

module Kernel
  def login
    Pages::Login
  end
end

这里还用到了一点Ruby元编程技巧,就是将页面模块封装为一个方法,并加入到Kernel模块下。这样做的好处就是,我们可以在项目的任意地方直接通过login.button_Login.click这样的形式来对控件进行操作了。

完成以上改造后,系统登录测试用例就可以采用如下形式进行编写了。

describe 'Login' do

  it 'with valid account' do
    # switch to My Account page
    my_account.button_My_Account.click
    inner_screen.has_control 'uiviewMyAccount'

    # enter login page
    my_account.button_Login.click
    inner_screen.has_control 'uiviewLogIn'

    # login
    login.field_Email_Address.type 'leo.lee@dji.com'
    login.field_Password.type '123321'
    login.button_Login.click
    inner_screen.has_control 'tablecellMyMessage'
  end

end

完整的代码请参考debugtalk/AppiumBooster3.RefactorV1分支

To be continued ...

经过这一轮重构,我们的测试用例与控件映射已经实现了分离,测试用例的可重用性与可扩展性也得到了极大的提升。

然而,在当前模式下,所有的测试用例仍然是以代码形式存在的,新增和修改测试用例时都需要到工程目录下编辑Ruby文件。

那有没有一种可能,我们只需要在表格中维护自动化测试用例(如下图),然后由代码来读取表格内容就可以自动执行测试呢?

是的,这就是我们对测试框架进行⎡工程化⎦改造的下一个形态,也就是AppiumBooster现在的样子。

在下一篇文章中,我们再进行详细探讨。

Read More ...

微信公众号:DebugTalk
原文链接:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-refactor-testcase-scripts/
《从0到1搭建移动App功能自动化测试平台》系列:http://debugtalk.com/tags/F0T1/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容