上一篇咱們講了Calabash的基本用法,有了上一篇的經驗,已經能夠寫基本的測試腳本了,只不過一些特殊狀況會寫的不那麼方便,這一篇咱們講一些Calabash的進階用法:大概是這幾個方向:java
上一篇咱們簡單介紹了Query的用法和經過Query來幫助咱們編寫測試腳本。 但這個是創建在人工查詢結果上,若是可讓腳本本身去查,是否是會更便捷? Calabash提供的預約義Steps中,只有極個別幾種View的Steps,相似ImageView等控件暫時尚未這種待遇,按照通常狀況,咱們只能選擇經過ID,或者人工Query查詢ImageView的index。那麼若是能夠只經過肉眼判斷當前ImageView的index就能夠編寫腳本是不更好呢?android
咱們來自定義一個Step:git
When /^I press imageView number (\d+)$/ do |index|
$imageView = query("android.support.v7.widget.AppCompatImageView")
touch($editText[index.to_i-1])
end
複製代碼
爲了保證Steps的天然語義,這裏咱們也對index作了減1操做。 你也看到了,在Steps的定義體中,是可使用Query語句的,不過這樣的寫法稍顯繁瑣了github
When /^I press imageView number (\d+)$/ do |index|
tap_when_element_exists("android.support.v7.widget.AppCompatImageView index:#{index.to_i-1}")
end
複製代碼
tap_when_element_exists
自己也支持必定程度的查詢功能。數組
你可能會問了,既然這些庫方法已經支持這樣的查詢了,在內部寫Query語句看起來也沒啥用了啊。下面咱們把Calabash預約義的Steps源碼拿來瞅瞅你就知道用處在哪裏了:ruby
Then /^I select "([^\"]*)" from "([^\"]*)"$/ do |item_identifier, spinner_identifier|
spinner = query("android.widget.Spinner marked:'#{spinner_identifier}'")
if spinner.empty?
tap_when_element_exists("android.widget.Spinner * marked:'#{spinner_identifier}'")
else
touch(spinner)
end
tap_when_element_exists("android.widget.PopupWindow$PopupViewContainer * marked:'#{item_identifier}'")
end
複製代碼
解釋一下:首先查詢是否有對應描述的下拉列表控件,查詢的結果是一個數組,若是結果是空的,那麼就調用點擊事件嘗試點擊一下,不出意外會報錯找不到這個控件。 若是不是空的,那就點擊這個查詢結果的第零個元素。 touch
方法,若是傳遞的參數是一個數組,會默認點擊第零個元素。 而後點擊彈出的PopupWindow中的item。bash
有幾個場景。app
以上場景的解決實質是,如何在不修改腳本的狀況下,能夠改變其中的參數? 其解決方案即是:引入環境變量。框架
新建一個新的自定義Step:ide
Then /^I enter \$([^\$]*) into input field number (\d+)$/ do |text_ev, index|
text = ENV[text_ev]
enter_text("android.widget.EditText index:#{index.to_i-1}", text)
end
複製代碼
有兩處改變,方法名中的參數接收處:\$([^\$]*)
和方法體中的參數獲取:text = ENV[text_ev]
這裏基本照貓畫虎就行了。
feature中這樣調用:
Then I enter $env_account_1 into input field number 1
複製代碼
$
後面的變量名是環境變量名。
在執行calabash-android run **.apk
前先設置環境變量
set env_account_1 123456
calabash-android run **.apk
複製代碼
這樣,在腳本執行過程當中,會將123456當作帳號填寫到輸入框中。
但這只是最基礎的用法,當須要的環境變量很是多時,再使用這樣的方式明顯不太合適,這個時候能夠將之放入ruby腳本中,也便於維護。
新建test_data1.rb
ENV["env_account_1"]="1111111111"
ENV["env_password_1"]="123456"
複製代碼
新建test_data2.rb
ENV["env_account_1"]="222222222"
ENV["env_password_1"]="123456"
複製代碼
終端中執行以下命令
Crowdsource-android git:(feature/testing) ✗ irb
irb(main):001:0> require './features/test_data1.rb'
=> true
irb(main):002:0>ENV["env_account_1"]
=> "11111111"
irb(main):003:0> exec('calabash-android run debug.apk ')
Feature: Login feature
...
#腳本執行完畢,切換另外一套環境變量
irb(main):001:0> require './features/test_data1.rb'
=> true
irb(main):002:0>ENV["env_account_2"]
=> "2222222"
irb(main):003:0> exec('calabash-android run debug.apk ')
Feature: Login feature
...
複製代碼
這樣就能夠整套整套的替換環境變量進行測試,ENV["env_account_2"]
命令用來查看環境變量的值。
關於Hooks,咱們在前面第二章有簡單提過一下。Hooks就是在監聽程序運行的某個階段,並作一些事情。在feature/support文件夾下有三個關於Hooks的文件: app_installation_hooks.rb , app_life_cycle_hooks.rb , hooks.rb
前兩個文件分別是對app安裝的Hooks,和app生命週期的Hooks。hooks.rb文件夾是空的,由咱們本身來編寫。 這裏暫時尚未作過什麼特別的實踐,我偷個懶,直接將 立成 @richardcao博客中的關於這一部分的段落摘了過來,原文在這裏:richardcao.me/2016/10/31/…。
咱們看一個簡單的app_life_cycle_hooks.rb理解理解:
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,這裏能夠看到,在主要用到了Before和After關鍵字進行操做,這個顯然很容易就看懂了,因而我本身寫了一個在每一個step執行以後都等待2秒的hook,下面的代碼寫在hooks.rb中(這個文件默認生成是空的):
require 'calabash-android/calabash_steps'
AfterStep do |scenario|
sleep 2
end
很容易對吧?關於這部分想多瞭解的能夠看cucumber wiki中的Hooks部分。
複製代碼
前面咱們提到了兩種自定義Calabash Steps的方法,分別是自定義Steps和封裝已有的Steps。還有第三種更爲深刻的定製化方案,那就是修改Calabash源碼自定義Actions,這裏的Actions指的即是咱們以前常常會看到的,相似這樣的代碼: perform_action('swipe', 'right') perform_action('tap_map_marker_by_title', marker_title, 60000)
其中swipe,tap_map_marker_by_title實際是方法名,他們的核心,以Calabash-android爲例,實際也是Android工程 java代碼實現的。若是你有看過其餘關於Calabash-android的博客,會有介紹修改其源碼,建立自定義的Actions方法的實例。
基本原理是:Calabash目錄/calabash-android/ruby-gem/test-server/instrumentation-backend
是一個Android工程,在其包/src/sh/calaba/instrumentationbackend/actions
中實現了咱們用到的ruby庫方法。 在其中建立咱們本身的Action類,而後進行編譯,便可建立咱們本身的庫方法並使用。
但這樣的博客都寫於2016年以前,目前最新的Calabash-android項目的源碼已經刪除了/calabash-android/ruby-gem/test-server
目錄下的文件夾,經過翻閱Git記錄得知:Calabash-android在2015.12.12日刪除了本地的test_server目錄,將其移動到了新的項目:calabash-android-server中。若是你將Calabash-android項目切換到2016年以前,還能夠找到test_server目錄,查看裏面的Android工程,修改並從新編譯。但我並不建議你這樣去作,使用這種方式擴展意味着要放棄 Calabash一年以上的更新進度。
calabash-android-server一樣是開源項目,雖然暫時我還沒搞明白如何在這個項目中修改源碼並應用到Calabash-android中,但只是單純研究其源碼仍是很是有價值的!
我挑了其中最簡單的一個類,咱們來研究一下:
../actions/gestures/ClickOnScreen.java
package sh.calaba.instrumentationbackend.actions.gestures;
import sh.calaba.instrumentationbackend.InstrumentationBackend;
import sh.calaba.instrumentationbackend.Result;
import sh.calaba.instrumentationbackend.actions.Action;
import android.view.Display;
public class ClickOnScreen implements Action {
@Override
public Result execute(String... args) {
Display display = InstrumentationBackend.solo.getCurrentActivity().getWindowManager().getDefaultDisplay();
float x = Float.parseFloat(args[0]);
float y = Float.parseFloat(args[1]);
int width = display.getWidth();
int height = display.getHeight();
InstrumentationBackend.solo.clickOnScreen((x/100)*width, (y/100)*height);
return Result.successResult();
}
@Override
public String key() {
return "click_on_screen";
}
}
複製代碼
先看下Steps的定義中是怎麼用這個方法的:
Then /^I click on screen (\d+)% from the left and (\d+)% from the top$/ do |x, y|
perform_action('click_on_screen', x, y)
end
複製代碼
public String key()
方法即是定義Action方法的名稱。public Result execute(String... args)
即是實現。
Result返回值表示該動做是否執行成功。若是失敗,會直接拋到終端中進行顯示錯誤信息。 這裏咱們重點關注這個對象:InstrumentationBackend.solo
獲取界面信息,以及真正實現點擊操做的,都是經過solo對象來實現的。那麼這個solo對象是個啥?
咱們摘取InstrumentationBackend
類的一段代碼:
...
import com.jayway.android.robotium.solo.SoloEnhanced;
import sh.calaba.instrumentationbackend.automation.CalabashAutomation;
import sh.calaba.instrumentationbackend.query.ui.UIObject;
import java.util.*;
/*
Utility class based on the current test-server life cycle.
*/
public class InstrumentationBackend {
private static final String TAG = "InstrumentationBackend";
public static List<Intent> intents = new ArrayList<Intent>();
private static Map<ActivityIntentFilter, IntentHookWithCount> intentHooks =
new HashMap<ActivityIntentFilter, IntentHookWithCount>();
private static CalabashAutomation calabashAutomation;
/* Instrumentation does not belong to this class. Here because of old architecture */
public static Instrumentation instrumentation;
public static SoloEnhanced solo;
public static Actions actions;
...
複製代碼
solo實際是com.jayway.android.robotium.solo.SoloEnhanced類。看到這裏應該明白了,Calabash-android對UI的操做核心其實藉助以Robotium來作的。
Robotium是一款國外的Android自動化測試框架,主要針對Android平臺的應用進行黑盒自動化測試,它提供了模擬各類手勢操做(點擊、長按、滑動等)、查找和斷言機制的API,可以對各類控件進行操做
哦,還要提一點,Calabash不支持跨進程的原有就在於Robotium不支持跨進程,因此讓Calabash支持跨進程的契機就在這裏,修改其源碼,調用uiautomator的API便可,具體可行性和方式還待探索,若是你有興趣,不妨咱們一塊兒研究呀
OK,大概就是這樣了,Calabash進階部分會隨時更新,修改Calabash-android-server源碼進行擴展的方法我也會繼續研究下去,成功之後會隨時更新博客。
《Calabash探索3-Calabash進階》