顯示具有 test 標籤的文章。 顯示所有文章
顯示具有 test 標籤的文章。 顯示所有文章

2011年12月10日 星期六

My iOS UI Automation Testing(自動化測試)

PastedGraphic-1
自動化測試是程式開發中,不可或缺的一環,而 xcode Instruments 裡面的 Automation tool ,便是 iOS UI 的自動化測試工具。
Automation tool 的測試腳本中,可以看得出來編譯完成的 APP,是符合 DOM(Document Object Model) 的。所以測試腳本,也很理所當然找了它最老的夥伴 - Javascript 來調用。

往下閱讀之前,建議一定要先看過...

DOM 雖然強大,但寫測試時卻又嫌囉嗦,要準確取得元件是一件很麻煩的事,看完上述連結,可以知道有兩種取得元件的方式:
  1. 用位置來取得,例如「window.buttons()[0]」,指的是第一個建立出來的 button。
  2. 用「name」來取得,例如「window.buttons()[“login”]」。
第一種方式,元件數量少還好,元件一多,會很容易指錯,而且測試程式碼可讀性幾乎是0,根本不知道到底取得的是哪一個元件。
第二種方式是比較理想的,但必須在 xib 上,或是 Objective-C 程式碼中,另外指定 accessibilityLabel 來命名 DOM 這邊所謂的「name」,也是挺麻煩的。
上述連結有這兩種方式的圖文說明。

突然想到之前寫 RSpec(Ruby 的自動測試框架) 測試 RoR 的時候,所有的操作,都只需要直接使用「肉眼所看到的字」,便可正確取得元件的文字或是進行操作(如點擊),於是便動手進行實作,簡化測試的程式。

https://github.com/alexvollmer/tuneup_js
借用了上述的測試框架,將自己寫的整合了進去,主要寫了三個 function
function haveContent(text) {

    var elements = window.elements();

    

    for (i = 0; i < elements.length; i++) {

        var element = elements[i];

        if (element.name() == text && element.isValid()) {

            return true;

        }

    }

    return false;

}

 

function click_button(name) {

    var buttons = window.buttons();

    

    for (i = 0; i < buttons.length; i++) {

        var button = buttons[i];

        if (button.name() == name) {

            button.tap();

        }

    }

}

 

function fill_in(name, text) {

    var textFields = window.textFields();

    

    for (i = 0; i < textFields.length; i++) {

        var textField = textFields[i];

        if (textField.name() == name) {

            textField.setValue(text);

        }

    }

}

haveContentclick_button 這兩個 function,只需指定肉眼看得到的文字就好,例如
test("Clear Hello", function(app, target) {

     click_button("Clear All");

     assertFalse(haveContent("Hello World"));

});

fill_in 則還是得在 xib 上,或是  Objective-C 程式碼中,另外指定 accessibilityLabel ,讓測試程式可以準確的指定到輸入框中,例如
test("Say Hello", function(app, target) {

     fill_in("name", "World");

     click_button("Say Hello");

     assertTrue(haveContent("Hello World"));

});

其實這三個 function 就可以搞定 95% 的測試了,其餘等需要時再補上,提供完整的範例下載

其它參考資源:

2011年9月11日 星期日

Ruby on Rails 的整合測試 (Integration testing)

撰寫自動化測試是寫程式非常重要的一環,在 Ruby 裡有許多的測試框架,但最多人推崇的非 RSpec 莫屬。

關於 RSpec 的介紹,可以參考:

 

但若要完成整合測試,光靠 RSpec 可能還是有點不足,這時我們可以配合 Capybara 做更完整的測試。

Capybara 會偷開 Server (rails server),然後讓測試程式真的訪問我們所寫的 Web,因此所得到的互動結果是真實發生的狀況。

這個網站有非常清楚關於 RSpecCapybara 的教學

http://opinionated-programmer.com/2011/02/capybara-and-selenium-with-rspec-and-rails-3/

 

實際寫起來可以像這樣

#encoding: utf-8
require 'spec_helper'
 
describe '活動頁面' do
  it '第一次進入此頁面,應該跳轉至註冊頁面' do
    visit '/campaigns'
    page.current_url.should =~ %r(http://[^/]+/sign_up)
  end
  
  it '實際註冊後,應該跳轉至登入頁面' do
    visit '/sign_up'
    fill_in 'user_email', :with => 'root@example.com'
    fill_in 'user_password', :with => 'root'
    fill_in 'user_password_confirmation', :with => 'root'
    click_on '註冊'
    
    page.current_url.should =~ %r(http://[^/]+/log_in)
  end
end

 

看了上面的例子,像不像在寫 spec 一樣呀?其實這個 spec 是可以拿來測試的喔!

 

其它

如果需要指定測試 Server 開啟在指定 port 的話,可以下達以下指令

Capybara.server_port = 3000

要回到 random port 的話,那麼可以…

Capybara.server_port = nil

 

2011/9/28 更新

記得把測試資料夾設定成 ./spec/requests/*_spec.rb,或是在每個測試項目加上「:type => :request」。如果資料夾不這麼命名,那就不會默認有「:type => :request」這個設置…成立一個新專案,要建置測試時出錯,找了一天才找到解法…

it 'xxxxxxxxxxxxx', :type => :request do
    # ......
end

 

參考資料

https://github.com/jnicklas/capybara/issues/483