RubyMotion 指南:测试

翻译:@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 的测试库要好用的多。性能

到底有多屌?学习

Unit Testing

使用 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

describeit 组成了一个用来实现测试的结构。在上面的这个describe结构中,传达了两个意思:"[Test that] Application",被测试对象有一Window。一个describe结构中能够包含多个itit中又能够包含多个测试断言。你若是喜欢的话,也能够写多个describe

每个将被测试的内容都会先执行before中的内容。应该把一些初始化的代码丢到这里。

接下来在终端中,运行rake spec,看看测试的结果。

image

仍是挂了……提示说没有找到window。让咱们在AppDelegate中修复:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
    @window.makeKeyAndVisible
    true
  end
end

再执行一次rake spec,此次正确了:

image

咦,彷佛已经经过自动化测试解决了一个Bug,希望是真的解决了吧。

咱们已经看到了如何去检查一个对象的属性,这颇有用但还不够。有时候咱们会触发事件,好比咱们敲击了一个按钮,它调用了一个内部方法,这种状况下,咱们要测试须要去手动调用这个方法,而后进行测试……事实上有更好的作法。

Funcational Testing

RubyMotion 对 Funcational Testing 有着很好的支持,好比你想测试触发 UI 事件,如TapSwipe 而后检查它的结果,你不用去button.callback.call,你能够用等效的方法:tap button,测试用例很整洁,对不对?

Funcational Testing虽然屌屌的,但它的局限是它只能测试一个UIViewController。因此像pushpop这种就无能为力了。须要注意。

要接着跑通这个例子,咱们须要一个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已经设置好这个属性。

最后,咱们把ControllerAppDelegate关联起来:

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.windowself.controller访问其。这样作也确保了被测试的Controller不会被干扰。

tap能够替换成flickdragpinch_closepinch_openrotate对应相应的行为。查看RubyMotion's full documentation具体的细节。

rake spec,它已经能正常工做啦:

2 specifications (2 requirements), 0 failures, 0 errors

总结

咱们看到了在RubyMotion中编写测试是多么的简单。若是你以前由于繁琐不写测试,如今没理由懒惰了。

咱们学到了啥?

  • RubyMotion加载测试用例从./spec目录。
  • 测试用例包含在describeit中,后面跟随着标签,用来标识和组织。
  • 使用 <any object>.should 进行断言。例:greeting.should == "hello"
  • UIViewControllers能够进行funcational。 tests,能够模拟一些像tappinch触发事件。在你的describe代码块中,使用tests <controller class>去使用这些特性。
  • tap <accessibility label>是访问View的accessibilityLabel属性。
相关文章
相关标签/搜索