3
\$\begingroup\$

The Model is simple: a Player class with three attributes: first_name, last_name, and team_id.

I'm just trying to get a handle on TDD for what will expand into a much more robust API. Here is my first stab at the integration/controller specs for REST actions on the API.

I haven't seen a lot of concrete examples of RSpec and API tests, so feedback is very welcome.

I'm using Rabl in my views for rendering the JSON Responses.

describe Api::V1::PlayersController do
  render_views

  before do
    @player1 = FactoryGirl.create(:player, first_name: "Joe", last_name: "Smith", team_id: 1)
    @player2 = FactoryGirl.create(:player, first_name: "Bob", last_name: "Jones", team_id: 2)
    @player3 = FactoryGirl.create(:player, first_name: "Peter", last_name: "Wilson", team_id: 3)
  end

  describe "#index" do

    before do
      get :index, :format => :json
    end

    it "should retrieve status code of 200" do
      response.response_code.should == 200
    end

    it "should retrieve license header" do
      response.header["X-LS-License"].should == "All Rights Reserved"
    end

    it "should retrieve application name header" do
      response.header["X-LS-Application"].should == "league-server"
    end

    it "should retrieve records-returned header" do
      response.header["X-LS-Records-Returned"].should be_present
    end

    it "should retrieve a content-type of json" do
      response.header['Content-Type'].should include 'application/json'
    end

    it "should retrieve list of players" do
      players = Player.all
      players.count.should == 3

      response.body.should include(@player1.id.to_s)
      response.body.should include(@player2.id.to_s)
      response.body.should include(@player3.id.to_s)
      response.body.should include('Joe Smith')
      response.body.should include('Bob Jones')
      response.body.should include('Peter Wilson')
    end
  end

  describe "#show" do

    before do
      get :show, id: @player1.id, :format => :json
    end

    it "should retrieve status code of 200" do
      response.response_code.should == 200
    end

    it "should retrieve application name header" do
      response.header["X-LS-Application"].should == "league-server"
    end

    it "should retrieve license header" do
      response.header["X-LS-License"].should == "All Rights Reserved"
    end

    it "should retrieve records-returned header" do
      response.header["X-LS-Records-Returned"].should == "1"
    end

    it "should retrieve a content-type of json" do
      response.header['Content-Type'].should include 'application/json'
    end

    it "should retrieve a single player" do
      player = Player.where id: @player1.id
      player.count.should == 1

      response.body.should include(@player1.id.to_s)
      response.body.should include('Joe Smith')
    end
  end

  describe "#create" do

    before do
      request_payload = {
        player: {
          first_name: "Buck",
          last_name: "Carradine",
          team_id: "1",
          junk: "trunk"
        }
      }

      post :create, request_payload
    end

    it "should retrieve status code of 200" do
      response.response_code.should == 200
    end

    it "should retrieve application name header" do
      response.header["X-LS-Application"].should == "league-server"
    end

    it "should retrieve license header" do
      response.header["X-LS-License"].should == "All Rights Reserved"
    end

    it "should retrieve records-returned header" do
      response.header["X-LS-Records-Returned"].should == "1"
    end

    it "should retrieve a content-type of json" do
      response.header['Content-Type'].should include 'application/json'
    end

    it "should retrieve a single player" do
      response.body.should include('Buck Carradine')
    end

    it "should not add extraneous attributes" do
      response.body.should_not include('junk')
    end
  end

  describe "#update" do

    before do
      request_payload = {
        player: {
          first_name: "Buck",
          last_name: "Carradine",
          team_id: "1"
        }
      }

      put :update, { id: @player1.id }.merge(request_payload)
    end

    it "should retrieve status code of 200" do
      response.response_code.should == 200
    end

    it "should retrieve application name header" do
      response.header["X-LS-Application"].should == "league-server"
    end

    it "should retrieve license header" do
      response.header["X-LS-License"].should == "All Rights Reserved"
    end

    it "should retrieve records-returned header" do
      response.header["X-LS-Records-Returned"].should == "1"
    end

    it "should retrieve a content-type of json" do
      response.header['Content-Type'].should include 'application/json'
    end

    it "should retrieve a single player" do
      response.body.should include('Buck')
      response.body.should_not include('Joe')
    end
  end

  describe "#delete" do

    before do
      delete :destroy, id: @player1.id
    end

    it "should retrieve status code of 200" do
      response.response_code.should == 200
    end

    it "should retrieve application name header" do
      response.header["X-LS-Application"].should == "league-server"
    end

    it "should retrieve license header" do
      response.header["X-LS-License"].should == "All Rights Reserved"
    end

    it "should retrieve records-returned header" do
      response.header["X-LS-Records-Returned"].should == "0"
    end

    it "should retrieve a content-type of json" do
      response.header['Content-Type'].should include 'application/json'
    end

    it "should retrieve a single player" do
      response.body.should include('Player deleted')
    end
  end
end
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

You might consider replacing this:

it "should retrieve status code of 200" do
  response.response_code.should == 200
end

with

it { response.response_code.should == 200 }

I find that very simple code checks are better done with the second form to eliminate duplication when you're reading the test.

It's helpful to review this occasionally. Something good is added frequently.

Quote from https://github.com/rspec/rspec-expectations

One-liners

The one-liner syntax supported by rspec-core uses should even when config.syntax = :expect. It reads better than the alternative, and does not require a global monkey patch:

describe User do it { should validate_presence_of :email } end

This is another helpful reference: https://www.relishapp.com/rspec/rspec-rails/v/2-13/docs/controller-specs

\$\endgroup\$
1
  • 2
    \$\begingroup\$ You could also make response the subject, and thus write its(:response_code) { should == 200 } \$\endgroup\$
    – Flambino
    Commented Feb 24, 2013 at 6:34
2
\$\begingroup\$

This looks pretty okay to me. I don't see why you have multiple header data tests though, I would assume they are all generated through the same piece of code, so you should either remove redundant tests to DRY up your specs, or if you are not using the same bit of code to generate the headers refactor that instead.

Regarding your initial question, I do not feel that API testing is any different that any other request and controller testing.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.