1. 問題背景
Scrum敏捷過程對交付質(zhì)量有著嚴(yán)格的要求:零缺陷的增量發(fā)布剂买。
要做到這一點(diǎn)意味著每個迭代發(fā)布都必須對 AC (Acceptance Criteria - 驗(yàn)收標(biāo)準(zhǔn)) 進(jìn)行全回歸胶滋,隨著迭代中增量交付的增加,全回歸的工作量與所需的時間也隨之增加筹陵。
我們在做APP開發(fā)過程中闷游,app開發(fā)人員 告訴我這app自動化測試搞不了惊奇,測試人員 也說,不好搞开仰,很麻煩拟枚,而且還得靠錄屏,blablabla.......
結(jié)果測試工作往往只能依賴人手進(jìn)行測試众弓,AC要靠人手來全回歸測試恩溅,就會發(fā)生這樣一種情景:
通常一個迭代的周期為兩周10個工作日。
- 迭代1谓娃, 計劃完成5個功能脚乡,相應(yīng)的AC也就不多,如果1天就能全部回歸滨达,那么開發(fā)有9天時間來實(shí)現(xiàn)功能奶稠。
- 迭代2, 計劃完成4個功能弦悉,相應(yīng)的AC加上迭代1的AC窒典,需要2天才能全部回歸,那么開發(fā)有8天的時間來實(shí)現(xiàn)功能稽莉。
- 迭代3瀑志,計劃完成3個功能,相應(yīng)的AC加上兩個迭代的AC污秆,可能需要3天才能全部回歸劈猪,那么開發(fā)有7天的時間來實(shí)現(xiàn)功能。
- ...
每次迭代AC全回歸的所需的時間在不斷增加良拼,而能用于開發(fā)的時間不斷在受到擠壓而減少战得,能交付的功能也就不斷在減少。
- 要質(zhì)量就沒速度庸推,如此還能敏捷的起來嗎常侦?
- 不少團(tuán)隊(包括我們)采用了犧牲交付質(zhì)量(僅部分回歸甚至無回歸)來確保開發(fā)進(jìn)度浇冰,這樣做既不符合敏捷過程的價值觀,實(shí)際上也沒有達(dá)到交付目標(biāo)聋亡。
- 還有一個土豪的做法肘习,就是增配測試。
我們一直在探索這個問題的解決方案坡倔,既想確保增量發(fā)布的速度漂佩,又想擁有交付質(zhì)量。只能想辦法把AC自動化起來罪塔,全回歸就不需要占用太多人手投蝉,而且還能隨時進(jìn)行,所需要的時間也大大縮短征堪。
2. 解決方案
在2017年1月份的ScrumMaster培訓(xùn)課中瘩缆,聊起App自動化測試有什么工具,便有小伙伴提了一下Appium请契】劝瘢回來后便查資料研究,發(fā)現(xiàn)有兩個非常不錯的自動化工具爽锥,一個就是Appium涌韩,另一個是阿里系的Macaca。這里不得不說氯夷,Macaca做的很不錯臣樱,javascript寫測試也相對容易上手。
然而腮考,做了對比后雇毫,我決定還是選用Appium。原因很簡單踩蔚,Appium支持Ruby和Cucumber棚放,我們的后端正好是用RubyOnRails寫的,后端開發(fā)人員已經(jīng)在寫后端過程中積累了許多編寫自動化測試用例的經(jīng)驗(yàn)馅闽。正好也可以來寫app的自動化測試飘蚯。
2.1 環(huán)境準(zhǔn)備
以下內(nèi)容的運(yùn)行環(huán)境為 MacOS 10.12 + Ruby 2.3.3 + NodeJS 7.x。
至于如何安裝 Ruby 和 NodeJS福也,這個應(yīng)當(dāng)為基本技能局骤,請自行搞定。
請不要問我如何在Windows上去搭建環(huán)境的有關(guān)問題暴凑,我只會建議要么白蘋果峦甩,要么黑蘋果,要么Ubuntu (不支持iOS)现喳。要么走您凯傲。
由于npmjs.org rubygems.org都在國外犬辰,訪問速度慢且不穩(wěn)定,建議先搭好梯子泣洞,或者改用國內(nèi)的源忧风。
2.1.1 Appium 1.6.3 安裝
npm install -g appium
npm install -g appium-doctor
# ios還需要安裝兩個工具 ios-deploy 用于安裝app到真機(jī);Carthage 用于安裝app到模擬器
brew install carthage
brew install ios-deploy
# brew 是個什么鬼球凰,如何安裝的?沒什么技術(shù)含量腿宰,請自行baidu
# 完成后可以運(yùn)行appium-doctor診斷一下環(huán)境是否正常
# 如果沒有android需求的呕诉,可以不用管 JAVA_HOME 與 android adb 等錯誤
appium-doctor
一切正常會得到類似這個信息
2.1.2 安裝 Appium 的 Ruby 驅(qū)動,和appium ruby console
gem install appium_lib
gem install appium_console
至此吃度,環(huán)境準(zhǔn)備工作已經(jīng)完成甩挫。
2.2 編寫自動化測試用例
測試用例采用Cucumber方式編寫,F(xiàn)eature 對應(yīng) 用戶故事椿每,Scenario 對應(yīng) AC伊者。
一個Feature包含多個Scenarios。
一個用戶故事有多個 AC驗(yàn)收標(biāo)準(zhǔn)间护。
至于Cucumber, 有空我會再寫一個文章亦渗。目前請先自行baidu之。
2.2.1 創(chuàng)建目錄
mkdir app_test
cd app_test
# 創(chuàng)建測試用例目錄
mkdir features
# 創(chuàng)建測試用例步驟目錄
mkdir features/steps
# 創(chuàng)建支持目錄
mkdir features/support
2.2.2 準(zhǔn)備Ruby Cucumber的Gemfile
$ cat Gemfile
source 'https://gems.ruby-china.org'
gem 'appium_lib', '~> 9.3.0'
gem 'rest-client', '~> 1.6.7'
gem 'rspec', '~> 2.14.1'
gem 'cucumber', '~> 1.3.15'
gem 'rspec-expectations', '~> 2.14.5'
gem 'spec', '~> 5.3.4'
gem 'sauce_whisk', '~> 0.0.13'
gem 'test-unit', '~> 2.5.5' # required for bundle exec ruby xunit_android.rb
安裝gem包
bundle install
2.2.3 cucumber env
$ cat features/support/env.rb
# This file provides setup and common functionality across all features. It's
# included first before every test run, and the methods provided here can be
# used in any of the step definitions used in a test. This is a great place to
# put shared data like the location of your app, the capabilities you want to
# test with, and the setup of selenium.
require 'rspec/expectations'
require 'appium_lib'
require 'cucumber/ast'
require 'sauce_whisk'
# Create a custom World class so we don't pollute `Object` with Appium methods
class AppiumWorld
end
# Load the desired configuration from appium.txt, create a driver then
# Add the methods to the world
caps = Appium.load_appium_txt file: File.expand_path('./', __FILE__), verbose: true
Appium::Driver.new(caps)
Appium.promote_appium_methods AppiumWorld
World do
AppiumWorld.new
end
# 結(jié)束時汁尺,推出驅(qū)動
at_exit { $driver.driver_quit }
# 在運(yùn)行標(biāo)有@reset_driver的Scenario前重啟驅(qū)動
Before("@reset_driver") do
$driver.restart
end
2.2.4 appium配置文件 appium.txt
關(guān)于appium.txt配置說明可以參考文檔 appium-server-capabilities
$ cat features/support/appium.txt
[caps]
platformName = "ios"
deviceName = "iPhone 6s"
platformVersion = "10.2"
app = '../ios/build/Build/Products/Debug-iphonesimulator/PuKe.app'
automationName = 'XCUITest'
language = 'zh'
locale = 'zh_CN'
[appium_lib]
sauce_username = false
sauce_access_key = false
2.3 編寫測試用例
每一用戶故事的測試用例編寫為一個feature法精。
2.3.1 編寫郵箱登陸用戶故事的AC
郵箱登陸用戶故事只是登陸故事中的其中一個, 故事ID US004
$cat features/US004_login_by_email.feature
Feature: US_004 郵箱登錄
為了正常使用需要登錄身份的功能
作為一個已經(jīng)用郵箱注冊過的用戶
我想要用郵箱和密碼登錄系統(tǒng)
@reset_driver
Scenario: AC_US004_02 登錄錯誤: 正確郵箱+錯誤密碼登錄
Given 我已經(jīng)用郵箱 test_user@mytest.com 與密碼 test123 注冊過賬號
When 我在 "主頁面" 點(diǎn)擊 "登錄/注冊" 進(jìn)入 "登錄頁面"
And 我在 "郵箱或手機(jī)" 輸入 "test_user@mytest.com"
And 我在 "密碼" 輸入 "b123456"
And 我按下按鈕 "登錄"
Then 我應(yīng)當(dāng)看到浮動提示 "用戶密碼不匹配"
Scenario: AC_US004_03 登錄錯誤: 沒有輸入用戶名和密碼
Given 我已經(jīng)用郵箱 test_user@mytest.com 與密碼 test123 注冊過賬號
And 我在 "郵箱或手機(jī)" 輸入 ""
When 我在 "密碼" 輸入 ""
And 我按下按鈕 "登錄"
Then 我應(yīng)當(dāng)看到浮動提示 "請?zhí)顚懲暾?
Scenario: AC_US004_04 登錄錯誤: 輸入用戶名沒有輸入密碼
Given 我已經(jīng)用郵箱 test_user@mytest.com 與密碼 test123 注冊過賬號
And 我在 "郵箱或手機(jī)" 輸入 "test_user@mytest.com"
And 我在 "密碼" 輸入 ""
When 我按下按鈕 "登錄"
Then 我應(yīng)當(dāng)看到浮動提示 "請?zhí)顚懲暾?
Scenario: AC_US004_01 正常郵箱+密碼登錄
Given 我已經(jīng)用郵箱 test_user@mytest.com 與密碼 test123 注冊過賬號
When 我在 "郵箱或手機(jī)" 輸入 "test_user@mytest.com"
And 我在 "密碼" 輸入 "test123"
And 我按下按鈕 "登錄"
Then 我應(yīng)當(dāng)?shù)竭_(dá) "主頁面"
And 等待 2 秒后退出
2.3.2 為feature編寫相應(yīng)的steps
Scenario 中的每個 Given When Then And 都在steps.rb 中有對應(yīng)的定義。
$ cat features/step_definitions/steps.rb
# These are the 'step definitions' which Cucumber uses to implement features.
#
# Each step starts with a regular expression matching the step you write in
# your feature description. Any variables are parsed out and passed to the
# step block.
#
# The instructions in the step are then executed with those variables.
#
# In this example, we're using rspec's assertions to test that things are happening,
# but you can use any ruby code you want in the steps.
#
# The '$driver' object is the appium_lib driver, set up in the cucumber/support/env.rb
# file, which is a convenient place to put it as we're likely to use it often.
# This is a different use to most of the examples; Cucumber steps are instances
# of `Object`, and extending Object with Appium methods (through
# `promote_appium_methods`) is a bad idea.
#
# For more on step definitions, check out the documentation at
# https://github.com/cucumber/cucumber/wiki/Step-Definitions
#
# For more on rspec assertions, check out
# https://www.relishapp.com/rspec/rspec-expectations/docs
Given(/^我已經(jīng)用郵箱 (.*) 與密碼 (.*) 注冊過賬號$/) do |email, password|
# sleep(1)
puts "DEBUG: email: #{email}"
puts "DEBUG: password: #{password}"
end
When(/^我在 "主頁面" 點(diǎn)擊 "登錄\/注冊" 進(jìn)入 "登錄頁面"$/) do
# 等待主頁面就緒, 主頁面ID 為 home_page
wait { id('home_page') }
# 點(diǎn)擊 主頁面中的 '登錄/注冊' 按鈕痴突,按鈕ID為 btn_to_login
id('btn_to_login').click
# 檢查頁面跳轉(zhuǎn)到 登錄頁面搂蜓, 登錄頁面ID為 page_login_account
wait { id('page_login_account') }
end
When(/^我在 "(.*?)" 輸入 "(.*?)"$/) do |input_field, input_value|
input_id = case input_field
when '郵箱或手機(jī)'
'input_username'
when '密碼'
'input_password'
else
'unknown'
end
input_box = id(input_id) # 定位指定的輸入框
input_box.clear # 清除原來的內(nèi)容
input_box.type "#{input_value}\n" # 輸入新內(nèi)容并回車
end
And(/^我按下按鈕 "登錄"$/) do
id('btn_login').click
end
Then(/^我應(yīng)當(dāng)看到浮動提示 "(.+)"$/) do |msg|
msg.strip!
puts "DEBUG: 期待 #{msg}"
wait { find(msg) }
end
Then(/^我應(yīng)當(dāng)?shù)竭_(dá) "主頁面"$/) do
wait { id('home_page') }
end
And(/^等待 (\d+) 秒后.*/) do |seconds|
sleep(seconds.to_i)
end
3. 運(yùn)行測試用例
3.1 運(yùn)行 Appium
運(yùn)行測試用例前必須先啟動appium,
新打開一個命令行窗口
$ appium
3.2 運(yùn)行測試用例
在 test_app 目錄中
$ cucumber features/US004_login_by_email.feature
3.3 觀看測試用例的運(yùn)行錄屏
http://v.youku.com/v_show/id_XMjUyMTkwODUzMg==.html
3.4 示例代碼參考
https://github.com/appium/sample-code/
4. 總結(jié)
拋開復(fù)雜的appium、cucumber steps辽装,發(fā)現(xiàn)沒帮碰,測試用例是不是很容易理解?
Scenario: AC_US004_01 正常郵箱+密碼登錄
Given 我已經(jīng)用郵箱 test_user@mytest.com 與密碼 test123 注冊過賬號
When 我在 "郵箱或手機(jī)" 輸入 "test_user@mytest.com"
And 我在 "密碼" 輸入 "test123"
And 我按下按鈕 "登錄"
Then 我應(yīng)當(dāng)?shù)竭_(dá) "主頁面"
And 等待 2 秒后退出
像上面的AC拾积,就算是非技術(shù)人員也能分分鐘寫出來殉挽,我們的產(chǎn)品經(jīng)理、UI設(shè)計師都在參與編寫AC殷勘,這就是我們選擇cucumber ruby的根本原因此再。
本來打算一篇寫完的,寫著寫著玲销,發(fā)現(xiàn)要寫的內(nèi)容太多了输拇,決定還是寫成一個系列:
- 首先、用Appium來測試贤斜,也存在一些坑要填策吠。
- 上面的feature和steps逛裤,為了演示好理解,做了相當(dāng)?shù)暮喕锬ā?shí)際上带族,我們的steps經(jīng)過一些重構(gòu),變得很通用靈活蟀给,后續(xù)放出來蝙砌。
- Appium的配置與基本指令
- 輔助神器 Appium Ruby Console (ARC) 的使用
- 如何用Cucumber編寫自動化測試
- React-Native類型的APP自動化有哪些坑需要繞過
有空的時候再接著寫 (二)、(三)跋理。择克。。前普。肚邢。。