翻譯:@shiweifu
本文連接:http://segmentfault.com/blog/shiweifu
原文連接:http://rubymotion-tutorial.com/8-testing/
目標讀者:["想了解RubyMotion開發模式", "想學習RubyMotion", "對移動端測試感興趣"]
翻譯者按:測試是移動開發的一個痛點。這篇文章講述了使用RubyMotion如何進行有效的測試,能夠看出相對於原生開發環境的測試,簡化不少。這部分也是RubyMoiton的一大特點。segmentfault
除了語法不一樣,你可能以爲 RubyMotion 不過是提供了一條使用 Ruby 語法來編寫 Cocoa 程序的方式。和使用 Objective-C 來實現一樣的功能相比,並沒有特殊之處。本章將介紹 RubyMotion 中獨有的特性。windows
自動化測試是屌炸天的事兒。屌在哪?它能使你的程序更加健壯,經過一些測試代碼,能讓你及時發現問題所在,是否是很棒?ruby
是的,你們都認爲測試是個好東西,但事實上大多數工程師並不能堅持寫測試。它不能讓你有足夠的成就感,不是能和其餘人吹噓的牛逼特性或者性能提高。但它是一種保障,特別是項目常常改變,它能讓你知道你的代碼究竟可不可用。app
接下來咱們將瞭解 RubyMotion 中的測試,簡單編寫,覆蓋度高。ide
在 Ruby 社區,你們都很重視測試,Ruby 的測試相對 iOS App,也相對簡潔。若是要在iOS 中實現自動化測試,每每須要藉助第三方庫或者使用JavaScript。RubyMotion 的測試庫要好用的多。性能
到底有多屌?學習
使用 motion create Tests
命令新建一個項目而後cd
進去。咱們討論裏面的spec
文件夾。測試
這個文件夾裏有一個名爲./spec/main_spec.rb
的文件,這是建立項目的時候自動生成的。在 RubyMotion 的測試工做的時候,它會加載這個文件夾裏面的*.rb
文件。咱們來看下這個文件的內容:ui
describe "Application 'Tests'" do before do @app = UIApplication.sharedApplication end it "has one window" do @app.windows.size.should == 1 end end
能夠看到一個簡單的表達式:@app.windows.size.should == 1
,它的意思看起來是若是.size
不爲1,就測試失敗。spa
.should
支持如下的判斷類型:
@app.nil?.should == false [1,2,3].should.not == [1,2,3,4] @model.id.should == example_id
describe
和 it
組成了一個用來實現測試的結構。在上面的這個describe
結構中,傳達了兩個意思:"[Test that] Application"
,被測試對象有一Window
。一個describe
結構中能夠包含多個it
,it
中又能夠包含多個測試斷言。你若是喜歡的話,也能夠寫多個describe
。
每個將被測試的內容都會先執行before
中的內容。應該把一些初始化的代碼丟到這裏。
接下來在終端中,運行rake spec
,看看測試的結果。
仍是掛了……提示說沒有找到window。讓咱們在AppDelegate
中修復:
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame) @window.makeKeyAndVisible true end end
再執行一次rake spec
,此次正確了:
咦,彷佛已經經過自動化測試解決了一個Bug,希望是真的解決了吧。
咱們已經看到了如何去檢查一個對象的屬性,這頗有用但還不夠。有時候咱們會觸發事件,好比咱們敲擊了一個按鈕,它調用了一個內部方法,這種狀況下,咱們要測試須要去手動調用這個方法,而後進行測試……事實上有更好的作法。
RubyMotion 對 Funcational Testing
有着很好的支持,好比你想測試觸發 UI 事件,如Tap
、Swipe
而後檢查它的結果,你不用去button.callback.call
,你能夠用等效的方法:tap button
,測試用例很整潔,對不對?
Funcational Testing
雖然屌屌的,但它的侷限是它只能測試一個UIViewController
。因此像push
和pop
這種就無能爲力了。須要注意。
要接着跑通這個例子,咱們須要一個UIViewController
的子類,建立./app/ButtonController.rb
,而後增長一個按鈕和回調事件。代碼以下:
class ButtonController < UIViewController def viewDidLoad super @button = UIButton.buttonWithType(UIButtonTypeRoundedRect) @button.setTitle("Test me title!", forState:UIControlStateNormal) @button.accessibilityLabel = "Test me!" @button.sizeToFit @button.frame = CGRect.new([10, 70], @button.frame.size) self.view.addSubview(@button) @button.addTarget(self, action:'tapped', forControlEvents:UIControlEventTouchUpInside) end def tapped p "I'm tapped!" @was_tapped = true end end
標準的代碼,除了accessibilityLabel
。這是每一個View
都包含的一個String
類型屬性。當使用VoiceOver功能的時候,系統依賴這個屬性(因此別隨便設置這個屬性的值)。爲啥咱們會提到這一點?由於 RubyMotion 的Funcational Testing
就依賴這個屬性。因此確保要測試的View已經設置好這個屬性。
最後,咱們把Controller
和AppDelegate
關聯起來:
def application(application, didFinishLaunchingWithOptions:launchOptions) @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame) @window.makeKeyAndVisible @view_controller = ButtonController.alloc.initWithNibName(nil, bundle:nil) @window.rootViewController = @view_controller true end
你可使用rake
執行一下,看看在你觸發按鈕事件的時候終端有沒有輸出I'm tapped!
。接下來咱們寫測試代碼,看看變量是否真的被賦值。
在main_spec.rb
,添加describe
塊及代碼:
describe "button controller" do tests ButtonController it "changes instance variable when button is tapped" do tap 'Test me!' controller.instance_variable_get("@was_tapped").should == true end end
讓咱們來看看有啥新東西。
tests <class>
鏈接咱們的describe
,指定UIViewController
。它的行爲很清晰:將要測試的UIViewController
放在了一個新的UIWindow
中。咱們能使用self.window
和self.controller
訪問其。這樣作也確保了被測試的Controller
不會被幹擾。
tap
能夠替換成flick
、drag
、pinch_close
、pinch_open
和rotate
對應相應的行爲。查看RubyMotion's full documentation具體的細節。
rake spec
,它已經能正常工做啦:
2 specifications (2 requirements), 0 failures, 0 errors
咱們看到了在RubyMotion中編寫測試是多麼的簡單。若是你以前由於繁瑣不寫測試,如今沒理由懶惰了。
咱們學到了啥?
./spec
目錄。describe
和it
中,後面跟隨着標籤,用來標識和組織。<any object>.should
進行斷言。例:greeting.should == "hello"
。UIViewControllers
能夠進行funcational。 tests,能夠模擬一些像tap
、pinch
觸發事件。在你的describe
代碼塊中,使用tests <controller class>
去使用這些特性。tap <accessibility label>
是訪問View的accessibilityLabel
屬性。