0

I want to test my boards_controller with RSpec. I stuck in equal validation of JSON response.

Here's simple boards_controller_spec.rb

require "rails_helper"

RSpec.describe Api::V1::BoardsController, type: :request do
  context "#index" do
    it "must return data" do
      get "/api/v1/boards"
      expect(response.body).to include_json(
        data: [
          id: (should be_kind_of Integer),
          link: (should be_kind_of String),
        ],
      )
    end
  end
end

And here's my boards_controller.rb that have only #index and #show methods.

module Api::V1
  class BoardsController < ApplicationController

    def index
      @boards = Board.all

      render json: @boards
    end

    def show
      @board = Board.find(params[:id])

      render json: @board
    end

    private

    def set_board
      @board = Board.find(params[:id])
    end

    def board_params
      params.require(:board).permit(:link)
    end
  end
end

On host:3000/api/v1/boards page i have this JSON response:

[
  {
    "id": 1,
    "link": "mzdbQBiKYk",
    "created_at": "2020-03-23T21:29:14.335Z",
    "updated_at": "2020-03-23T21:29:14.335Z"
  },
  {
    "id": 2,
    "link": "ZkbspsYIPz",
    "created_at": "2020-03-23T21:29:14.347Z",
    "updated_at": "2020-03-23T21:29:14.347Z"
  }
]

So my goal is to check that id is integer and link is string, but when i trying to run test i have this error:

Failures:

  1) Api::V1::BoardsController#index must return data
     Failure/Error: id: (should be_kind_of Integer),
       expected #<Api::V1::BoardsController:0x0000561c114a6e28 @_routes=nil, @_request=nil, @_response=nil> to be a kind of Integer
     # ./spec/requests/boards_controller_spec.rb:9:in `block (3 levels) in <top (required)>'

What's wrong here? Boards was created with seed (only generated random links for boards), because i do not need create method for this model.

5
  • 1
    You have an expectation inside the expectation. That doesn't make any sense. You only need to have one expectation. In fact, more precisely, your test is failing because you're effectively saying: expect(subject).to be_kind_of(Integer). Commented Mar 26, 2020 at 12:04
  • That line should be something more like: id: kind_of(Integer) Commented Mar 26, 2020 at 12:06
  • Thank you, @TomLord! But expect(id).to kind_of(Integer) throws another error. id is not available from within an example..... First i need to get that id from JSON response? Commented Mar 26, 2020 at 12:09
  • That's not what I said to write Commented Mar 26, 2020 at 13:44
  • I said you don't need an expectation inside the expectation. You only need to write "expect" once in the whole test. Commented Mar 26, 2020 at 13:44

1 Answer 1

2

The includes_json matcher is used to match the actual values of the JSON.

RSpec.describe Api::V1::BoardsController, type: :request do
  context "#index" do
    it "must return data" do
      get "/api/v1/boards"
      expect(response.body).to include_json(
        data: [
          id: 1,
          link: "mzdbQBiKYk"
        ]
      )
    end
  end
end

Check against the values of the records that you have setup during the setup phase of the test or your fixtures if you're using that junk.

If you wanted to check the type you would need to manually traverse the JSON:

RSpec.describe Api::V1::BoardsController, type: :request do
  let(:json) { JSON.parse(response.body) }
  context "#index" do
    it "must return data" do
      get "/api/v1/boards"
      expect(json["data"].first["id"]).to be_kind_of Integer
    end
  end
end

But that's not really a very good test as you can actually test that it contains the right data instead. Also you have a weird unidiomatic combination of controller spec and a request spec. A request spec should look like this:

RSpec.describe 'Boards API V1', type: :request do
  describe "GET /api/v1/boards" do
    let!(:boards) do
       # @todo setup data for the test
    end

    it "must return data" do
      get "/api/v1/boards"
      expect(response.body).to include_json(
        data: [
          id: boards.first.id
          link: boards.first.link
        ]
      )
    end 
  end
end

A good request spec describes the HTTP endpoints you are testing. Not the underlying controller.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your answer with example <3

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.