Constructing Basic Tests

The simplest tests are designed to test just one component of our code at a time, independent of how it interacts with other classes or the global state of our app. Tests consist of simple assertions such as object equality or existence. If any of our assertions fail, then the entire test fails and stops executing. We’re going to write some simple tests for an app and go through some common use cases you might run into.

Let’s create a new RubyMotion project with motion create TestFun. Remember way back in Chapter 1, Creating a New App when we discussed the default files and folders motion create generates? Well, we’re going to take a look at one folder we previously neglected: spec.

RubyMotion reads your tests from the spec folder, recursively loading every single rb file it contains. One way to organize your tests is to make each file correspond to a class in your code.

When you create a RubyMotion app, it generates a default test in spec/main_spec.rb that looks like this:

 describe ​"Application 'TestFun'"​ ​do
  before ​do
  @app = UIApplication.sharedApplication
 end
  it ​"has one window"​ ​do
  @app.windows.size.should == 1
 end
 end

Sit there for a moment and imagine every word of that being read out loud. See how expressive and simple that is? @app.windows.size.should == 1 does exactly what it sounds like: if size is not equal to 1, then the test fails.

should is a helper method added to every object in the testing environment and forms the basis of most tests. It uses the default meaning of the == operator for each object type, so it can be used for more than just integers. Here are some more examples:

 @user.nil?.should == ​false
 
 @model.id.should == 5
 
 [1,2,3].should.not == [1,2,3,4]

The last example uses the not helper method, which negates the intention of should. We could have just used should with an additional ! on the right side, but chaining a not method improves readability.

The generated test in main_spec also has the describe and it blocks, which add structure to our tests. You can read the combination of the two blocks like a sentence: “Test that the application ’TestFun’ has one window.” A describe block can have many it blocks, and an it block can have many assertions. You can even nest describes to create very granular descriptions of each test. We’ll see the results of all this structuring in just a second when we run the tests.

The last element of the generated test is the before block. The code in a before block runs prior to every sibling test block. It’s a good place to reset your objects and restore the state of the code you’re testing. Similarly, you can define an after block to execute after every test.

So, how do we actually use all these fun toys? In your terminal window, run rake spec. This will build your app and run the specs against it. In addition to the app briefly appearing in the simulator, you should see some output in the terminal like this:

  $ ​​rake​​ ​​spec
  Build ./build/iPhoneSimulator-7.1-Development
  ...
  Simulate ./build/iPhoneSimulator-7.1-Development/TestFun_spec.app
 
 Application 'TestFun'
  - has one window [FAILED]
 
 Bacon::Error: 0.==(1) failed
  spec.rb:553:in `satisfy:': Application 'TestFun' - has one window
  ...
 
 1 specifications (1 requirements), 1 failures, 0 errors

RubyMotion will run all of our tests and yell if something goes wrong. You can see here how the test output reflects the describe blocks (“Application ’TestFun’”) and the it blocks within them (“- has one window”).

The generated test actually fails! The lone assertion that we have a UIWindow fails, as indicated by the [FAILED] beside the test description and the summary at the bottom. One current headache in RubyMotion is that the stack trace does not reveal the line number in our original main_spec; thankfully, we can deduce it from the spec description and assertion type.

Let’s fix this test. In our AppDelegate, we add a UIWindow just like in all of our previous apps.

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

Run rake spec again and give yourself a pat on the back.

 $ ​​rake​​ ​​spec
 ...
 1 specifications (1 requirements), 0 failures, 0 errors

You can actually get pretty far just by adding new test files to spec the few RSpec methods we’ve covered (such as describe and should). Tests involving models or algorithmic code should be completely expressible with even these basic methods. But unfortunately, iOS apps aren’t just static bits of code: we often need to test our callback-centric user interface.

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

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