Models Tests

Let's check how we can test models. This process is even easier than Screen testing. You start with the same code:

    describe CoolModel do
      context "some context" do
        before do
          CoolModel.destroy_all
        end

        it "should do something" do
          # ...
        end
      end
    end
    

We don't have lots of models in the app so we will test Forecast model's for_location(location, on_date: date, &block) method. It may have two different results: if a response is successful and it contains forecast, we call a block and pass a forecast to it with an empty error value. If something went wrong, forecast value passed to the block would be empty, and error will have a description of what happened.

In this example, we will learn how to work with asynchronous tests. We are going to add a delay to a mocked request, and then wait for a response:

    describe Forecast do
      extend WebStub::SpecHelpers
      extend AppConstants

      def date_to_string(date)
        date_formatter = NSDateFormatter.alloc.init
        date_formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ"
        date_formatter.stringFromDate(date)
      end

      context "Requests" do
        it "should return an error if the request failed" do
          # create date for a request:
          date = date_to_string(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(status_code: 500, delay: 0.1)

          Forecast.for_location({lat:0.0, lng: 0.0}, on_date: date) do |forecast, error|
            forecast.should.be.kind_of(NilClass)
            error.should.be.kind_of(NSError)

            resume
          end
          wait do
            stub.requested?.should.be.equal(true)
          end
        end
      
Test code should be easy to understand. We have created date_to_string method because we will convert a date into a string with a given format multiple times during Forecast tests.

First we make a string from a date with a format that forecast.io can understand. Then we mock a request to a URL with a given date and location and making an actual request by calling for_location(location, on_date: date, &block) method. You may notice new status_code and delay parameters in a stub. We use response status code 500 because we want a response with an error (successful responses have status code 200 or similar). The delay is to make request asynchronous to learn how to work with wait macros.

When a response is received, a block will be called with a forecast and error variables. Since the response has an error, the forecast should not exist, and error variable should have an error description. Then we call resume, which will start the code inside wait macros. If the resume isn't be called for a long period test will fail. To change waiting time you can call wait like this:

        wait 0.5 do
          # some code
        end
      
.

And let's add a test for a successful response. It will look almost exactly like a first test:

    it "should call a block if request successful" do
      # create date for a request:
      date = date_to_string(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" }]
            }
          },
          delay: 0.1)

      Forecast.for_location({lat:0.0, lng: 0.0}, on_date: date) do |forecast, error|
        forecast.should.be.kind_of(Hash)
        error.should.be.kind_of(NilClass)

        resume
      end
      wait do
        stub.requested?.should.be.equal(true)
      end
    end
      
Let's run them with rake spec:

We have some main tests created, so let's commit all the changes:

    git add .
    git commit -m "test added"
    

Summary

In this chapter we learned how to test models, improved our skills in stubbing and mocking, and I think we are ready to finally finish superapp!

Book Index | Next