Stubbing

In tests we often need to fake some data: it can be server response, values returned from the method or any other thing. It called "stubbing".

Let's use server response as an example. When testing networking, we need to test successful responses, unsuccessful responses, responses with some particular error, timeouts, etc. It is not that important whether real server returns these responses, or it is just a fake hardcoded data. Therefore, before test we stub a request, and we tell it what we expect to receive.

Stub here means that test won't wait for a response from the server. Each time app will try to reach some URL stubbing library will return a predefined data instead of waiting for a real response. It will save you a lot of time - you can make tests return whatever value you want - successful responses, responses with some particular data or status code, and much more.

To stub a response we need to provide exact URL, HTTP method (GET, POST, PUT, DELETE), and what response we want to receive. Then when your app will do a request to a given URL, it will return your fake response instead of a real server's response, and then you can test how your app will behave with a provided response.

Method stubbing works similar - you provide a method name that you want to stub and a value that you want it to return:

        mood = Mood.new
        mood.level
        # => nil

        # stub `level` mathod to return whatever we want:
        mood.stub!(:level, 4)
        mood.level
        # => 4
      
This may sound weird and useless at this moment, but in the future, we will use it a lot. Some methods won't be called during tests, or some methods will need a lot of customization to return the value that we need, so in those cases, it will be easier to make it return whatever we want to test how the app behaves.

First we need to update our Gemfile, it should include two new gems: webstub and motion-stump:

        group :spec do
          gem 'webstub'         , '1.1.2'
          gem 'motion-stump'    , '0.3.2'
          gem 'motion-redgreen' , '1.0.0'
        end
      
webstub also has to be included in the test file that will be using it like this:
        describe "Mood Selector Screen" do
          extend WebStub::SpecHelpers
          tests MoodSelectorScreen
          extend AppConstants

          def screen
            window.rootViewController
          end

          # rest of the test ...
        end
        
It is time to install these gems with bundle install.

Ok, let's try to make a test for a successful Mood creation. When MoodSelectorScreen appears, we will get user's location and some forecast for it. Then tap on an "Awesome" button will be simulated, and a number of Mood objects should be increased by one.

To simulate user's location, we will call manually locationManager(manager, didUpdateLocations: locations) method with predefined location value. This method saves location and will get a forecast for it. To make web request work, we will mock it to return a correct forecast without requesting a real server. Then we will use tap method to simulate Mood creation:

        it "should create new Mood object" do
          # fake location object:
          location_manager = CLLocationManager.alloc.init
          location = CLLocation.new
          location.coordinate.latitude = 0
          location.coordinate.longitude = 0

          # create date for a request:
          date_formatter = NSDateFormatter.alloc.init
          date_formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ"
          date = date_formatter.stringFromDate(NSDate.now)

          # mock request:
          url = "#{AppConstants::FORECAST_API_URL}#{AppConstants::FORECAST_API_KEY}/0.0,0.0,#{date}"
          stub = stub_request(:get, url).
            to_return(
              json: {
                "hourly" => {
                  "data" => [{ "temperature"=>30.00, "icon"=>"clear-night" }]
                }
              })

          # start location and forecast actions:
          screen.locationManager(location_manager, didUpdateLocations: [location])

          # test changes:
          mood_count = Mood.count
          tap("Awesome")
          Mood.count.should.be.equal(mood_count + 1)
          stub.requested?.should.be.equal(true)
        end
        
As you can see this test has five main steps: With next rake spec run you will see something like this:

Summary

You can find more info on request stubbing at webstub docs.

To learn more on stubbing and mocking objects, please visit motion-stump docs.

Book Index | Next