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:
locationManager(location_manager, didUpdateLocations: [location])
yyyy-MM-dd'T'HH:mm:ssZZ
format.
locationManager(location_manager, didUpdateLocations: [location])
method with a created earlier location and location manager -
it will save a location and will get a forecast for us.
rake spec
run you will see something like this:
You can find more info on request stubbing at webstub docs.
To learn more on stubbing and mocking objects, please visit motion-stump docs.