Search code examples
ruby-on-railsrubyrspecrspec-rails

RSpec controller spec: How to test rendered JSON?


I'm trying to test a simple controller's action of a Rails API

Here's the controller in question:

class Api::TransactionsController < ApplicationController
  def index
    transactions = Transaction.all
    json = TransactionSerializer.render(transactions)
    render json: json
  end
end

Here are my specs so far

require 'rails_helper'

RSpec.describe Api::TransactionsController do
  describe '.index' do
    context "when there's no transactions in the database" do
      let(:serialized_data) { [].to_json }

      before { allow(TransactionSerializer).to receive(:render).with([]).and_return(serialized_data) }
      after { get :index }

      specify { expect(TransactionSerializer).to receive(:render).with([]) }
      specify { expect(response).to have_http_status(200) }
    end
  end
end

I want to test the response. Something like in this Stack Overflow question How to check for a JSON response using RSpec?:

specify { expect(response.body).to eq([].to_json) }

My problem is that response.body is an empty string. Why is that ?


Solution

  • Not sure what kind of serializer you're using. But, render is not a method on an ActiveModel::Serializer. Try this instead:

    module Api
      class TransactionsController < ApplicationController
        def index
          transactions = Transaction.all
          render json: transactions
        end
      end
    end
    

    If your TransactionSerializer is an ActiveModel::Serializer, Rails will, by convention, just use it to serialize each Transaction record in the ActiveRecord::Relation.

    And, test it like this:

    require 'rails_helper'
    
    describe Api::TransactionsController do
      describe '#index' do
        context "when there's no transactions in the database" do
          let(:transactions) { Transaction.none }
    
          before do
            allow(Transaction).to receive(:all).and_return(transactions)
    
            get :index
          end
    
          specify { expect(response).to have_http_status(200) }
          specify { expect(JSON.parse(response.body)).to eq([]) }
        end
      end
    end
    

    Part of the problem here might have been that you weren't actually calling get :index until after the tests ran. You need to call it before the tests run.