Skip to content

Running & writing specs

bmorton edited this page Jan 30, 2012 · 1 revision

RestKit Test Environment

RestKit ships with a testing infrastructure built around OCUnit and a Ruby testing server environment built on Sinatra. To be able to run the tests, you need to do a little bit of setup:

  1. Install the Ruby Bundler Gem (if necessary): gem install bundler
  2. Install the other required Gems via Bundler: bundle
  3. Start the spec server: rake spec:server
  4. Build and execute the tests within Xcode via the Product > Test

If the project builds the Specs target correctly and executes the suite, then you are all set. If there are any issues, you may need to reach out to the mailing list for help debugging.

Running Tests

By default, all tests will be executed when your run the Test build action. You can selectively enable/disable which tests are run by holding down the Option Key when selecting Product > Test (or type Apple+Option+U).

Writing Tests

RestKit tests are divided into two portions. There are pure unit tests, which only require the test harness to be configured and there are integration tests that test the full request/response life-cycle. In general, testing RestKit is very straight-forward. There are only a few items to keep in mind:

  1. Tests are implemented in Objective-C and run inside the Simulator or on the Device.

  2. Test files live in sub-directories under Specs/ appropriate to the layer the code under test belongs to

  3. Tests begin with "test" and should be camel-cased descriptive. i.e. testShouldConsiderA200ResponseSuccessful

  4. Expectations are provided using OCHamcrest. Details of the matchers are available on the OCHamcrest Github Page. Generally the matchers are of the form:

     assertThat([someObject someMethod], is(equalTo(@"some value")));
    

    There is a corresponding isNot method available as well.

  5. The RKSpecEnvironment.h header includes a number of helpers for initializing and configuring a clean testing environment.

  6. OCMock is available for mock objects support. See http://www.mulle-kybernetik.com/software/OCMock/ for details

  7. RestKit is available for 32bit (iOS) and 64bit (OS X) platforms. This introduces some complexity when working with integer data types as NSInteger and NSUInteger are int's on 32bit and long's on 64bit. Cocoa and OC Hamcrest provide helper methods for dealing with these differences. Rather than using the Int flavor of methods (i.e. [NSNumber numberWithInt:3]) use the Integer flavor (i.e. [NSNumber numberWithInteger:]). This will account for the type differences without generating warnings or requiring casting.

Writing Integration Tests

RestKit ships with a Sinatra powered specs server for testing portions of the codebase that require interaction with a web service. Sinatra is a simple Ruby DSL for defining web server. See the Sinatra homepage for more details.

The specs server is built as a set of modular Sinatra application in the Specs/Server subdirectory of the RestKit distribution. When you are adding new integration test coverage to the library, you will need to create a new Sinatra application to serve your needs. By convention, these are namespaced by functional unit for simplicity. For example, if we are adding a new cacheing component to the application and want to test the functionality, we would:

  1. Create a new file at Server/lib/restkit/network/cacheing.rb

  2. Create a namespaced Ruby class inheriting from Sinatra::Base to suit our purposes:

     module RestKit
       module Network
         class Cacheing < Sinatra::Base
           get '/cacheing/index' do
             "OK"
           end
         end
       end
     end
    
  3. Open restkit.rb and add a require for the cacheing server:

     require 'restkit/network/cacheing'
    
  4. Open server.rb and add a use directive to the main spec server to import our module:

     class RestKit::SpecServer < Sinatra::Base
       self.app_file = __FILE__
       use RestKit::Network::Authentication
       use RestKit::Network::Cacheing
    

You now have a functional server-side component to work with. Consult the Sinatra documentation for example on setting response headers, MIME types, etc. It's all very simple and low ceremony.

You can now switch to the RestKit sources and look the in Specs directory. Keeping with the cacheing example, we would create a new RKCacheingSpec.m file and pull in RKSpecEnvironment.h. From there we can utilize RKSpecResponseLoader to asynchronously test the entire request/response cycle. The response loader essentially spins the run-loop to allow background processing to execute and simulate a blocking API. The response, objects, or errors generated by processing the response are made available via properties on the RKSpecResponseLoader object.

Let's take a look at an example of how to use the response loader to test some functionality:

 - (void)testShouldFailAuthenticationWithInvalidCredentialsForHTTPAuthBasic {
    RKSpecResponseLoader* loader = [RKSpecResponseLoader responseLoader];
    RKClient* client = [RKClient clientWithBaseURL:RKSpecGetBaseURL()];
    client.username = RKAuthenticationSpecUsername;
    client.password = @"INVALID";
    [client get:@"/authentication/basic" delegate:loader];
    [loader waitForResponse];
    assertThatBool([loader.response isOK], is(equalToBool(NO)));
    assertThatInt([loader.response statusCode], is(equalToInt(0)));
    assertThatInt([loader.failureError code], is(equalToInt(NSURLErrorUserCancelledAuthentication)));
  }

That's really all there is to it. Consult the existing test code in Specs/ for reference.

Happy Testing!