Calabash探索3-Calabash進階

2017-3-17 | 暴打小女孩| 測試

前言

上一篇咱們講了Calabash的基本用法,有了上一篇的經驗,已經能夠寫基本的測試腳本了,只不過一些特殊狀況會寫的不那麼方便,這一篇咱們講一些Calabash的進階用法:大概是這幾個方向:java

  • 在自定義的Steps中使用Query語句。
  • 自定義Steps支持環境變量擴展。
  • Hooks。
  • Calabash源碼修改與擴展。

在自定義的Steps中使用Query語句

上一篇咱們簡單介紹了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

自定義Steps支持環境變量擴展

有幾個場景。app

  • 若是測試不一樣的系統或者不一樣分辨率的手機,測試用例內部可能須要細微改變,爲了應對這些改變,複製多套腳本進行修改顯然不是特別合適。
  • 不一樣帳號有不一樣的權限和數據,同一個測試用例可能須要不一樣的帳號來測試,爲了應對不一樣帳號複製多套腳本進行修改顯示不是特別合適。
  • 初期想要讓android和iOS無縫的使用一套腳本不太現實,兩套腳本幾乎必然,但若是遇到上面的現象,測試中須要使用一套數據進行支持(帳號),這個時候兩邊都維護一套如出一轍的數據就不太合適了。

以上場景的解決實質是,如何在不修改腳本的狀況下,能夠改變其中的參數? 其解決方案即是:引入環境變量。框架

新建一個新的自定義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,咱們在前面第二章有簡單提過一下。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部分。

複製代碼

cucumber wiki中的Hooks部分

Calabash源碼修改與擴展

前面咱們提到了兩種自定義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探索1-Run Calabash》

《Calabash探索2-Calabash用法詳解》

《Calabash探索3-Calabash進階》

《Calabash探索4-Calabash踩坑總結》

相關文章
相關標籤/搜索