Testing App UI and Controllers

In addition to a basic RSpec testing framework, RubyMotion includes extensions that help us test complex interactions with views and a device. You can specifically test events such as taps, flicks, and pinches to ensure that your callbacks are functioning accordingly.

Let’s walk through an example of RubyMotion’s UI test helpers. We’re going to programmatically tap a UIButton and assert that some state has changed after the tap. Even better, we’ll make sure to visibly alter the UI after the tap to make it obvious while the test is running in the simulator.

Since UI testing requires a UIViewController instance, we need one of our own. Create the app/controllers directory and a button_controllerrb file within. Our implementation doesn’t have anything surprising; we’re just creating a button in viewDidLoad.

 class​ ButtonController < UIViewController
 def​ viewDidLoad
 super
  button = UIButton.buttonWithType(UIButtonTypeSystem)
  button.setTitle(​"Tap Me"​, forState​:UIControlStateNormal​)
  button.sizeToFit
  button_origin = [0, 100]
  button.frame = [button_origin, button.frame.size]
  button.addTarget(self,
  action​:'tapped'​,
  forControlEvents​:UIControlEventTouchUpInside​)
  self.view.addSubview(button)

After tapping, we need to change some variable of ButtonController. Let’s add a quick and dirty instance variable before finishing viewDidLoad.

 self.view.addSubview(button)
 
»@tapped = ​false

The button callback will not only change @tapped but change the view’s background color as well. When the specs run, we’ll see the app flash colors and know for sure that the RubyMotion UI helpers are working as intended.

 def​ tapped
  @tapped = ​true
  self.view.backgroundColor = UIColor.redColor
 end

Finally, we need to hook up the controller in our AppDelegate. Simply set a new instance as the rootViewController, give it a rake, and make sure it looks like Figure 8, Preparing our UI test after tapping the button.

 @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
 @window.backgroundColor = UIColor.whiteColor
 @window.rootViewController = ButtonController.alloc.initWithNibName(​nil​, bundle​:nil​)
 @window.makeKeyAndVisible
images/testing/tapped.png

Figure 8. Preparing our UI test

Now that our UI is ready, it’s time to write the actual test. Since we’re keeping our project organized, create spec/button_controller_specrb to store our test. Let’s look at how we implement this, since it’s quite short.

 describe ​"ButtonController"​ ​do
  tests ButtonController
  it ​"changes color after tapping"​ ​do
  tap(​"Tap Me"​)
  controller.instance_variable_get(​"@tapped"​).should == ​true
 end
 end

All that earlier code for these few lines? Indeed, UI testing is just as expressive as more basic tests. Each UI test focuses on a single UIViewController and will actually instantiate a new UIWindow containing one isolated controller. The tests method after describe is what triggers this behavior; simply pass it the appropriate class, and RubyMotion handles the rest.

By declaring our test to be a UI test, we get some new helper methods. Here we use tap, which takes either an instance of UIView or an accessibility label. Accessibility labels are used by the iOS VoiceOver utility to read-aloud parts of the screen. You can set these labels yourself by changing the accessibilityLabel property of view, but some classes can derive their label value from other properties such as title. UIButton is one such class, so we can safely pass "Tap Me" to tap.

If we run rake spec, we’ll see the test run in the simulator and the background momentarily flash red. We can confirm in the console that our ButtonController test does pass, but something has gone wrong in our original test!

 $ ​​rake​​ ​​spec
 ...
 
 ButtonController
  - changes color after tapping
 
 Application 'TestFun_ui'
  - has one window [FAILED]
 
  ...
 2 specifications (2 requirements), 1 failures, 0 errors

We briefly went over how RubyMotion runs each controller test in isolation by creating a new UIWindow; thus, our test that there is only one window should indeed fail. If you want, you can rewrite the test to confirm that we have a rootViewController instead.

 it ​"has one controller"​ ​do
  controller = @app.keyWindow.rootViewController
  controller.is_a?(ButtonController).should == ​true
 end

That wraps it up for our little testing introduction. We’ve gone over the skills to write solid tests for your apps, but it is up to you to put them in action. For detailed information on the testing framework, check out the RubyMotion Developer Center’s article on testing.[17]

Testing is one topic where I think RubyMotion really shines. Objective-C testing frameworks are nontrivial to set up in Xcode, and Apple’s prescribed testing solution isn’t nearly as clean as RubyMotion’s RSpec-like format. Plus, since tests are just a call to rake spec away, it’s very easy to integrate RubyMotion into a continuous integration environment.

Now that we can build and test the typical iOS UI, it’s time to put it all together and hook it up to the Internet. In Chapter 7, Example: Writing an API-Driven App, we’re going to create a small example app that gets and sends data via an HTTP API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset