Search code examples
ruby-on-railsrubyamazon-s3rspecrspec-rails

Stub or mock instance method rspec


The below function is a controller action and gets the data to be served from an AWS S3 bucket.

def show
    s3_response = Zlib::GzipReader.new(ApiBucket.bucket.object(id).get.body).read
  render json: s3_response
end

I am writing a spec for this method. I need to stub/mock such that s3_response doesn't actually request from s3. Here is what I tried. This doesn't work though. Please help.

describe '#GET show' do
  let!(:resource) { create(:resource) }

  before do
    json_data = Api::V2::Presenter.consume_as_json(resource)
    allow_any_instance_of(Zlib::GzipReader).to receive(:read).and_return(json_data)
  end

  it 'should return the resource in page format' do
    get :show, format: :json, params: { id: resource.uuid }
    response_body_json = JSON.parse(response.body)
    json_data = Api::V2::Presenter.consume_as_json(
        Api::V2::ResourcePresenter.new(resource).page,
        true
      )
    expect(response_body_json).to eql(JSON.parse(json_data))
  end
end

I am getting this error Zlib::GzipFile::Error: not in gzip format


Solution

  • Instead of stubbing Zlib::GzipReader or the S3 Bucket. One simple and easy way to handle such cases would be to create a new private function in the controller and then stub the controller function.

    In the controller side:

    def show
      render json: s3_response
    end
    
    private:
    
    def s3_response
       Zlib::GzipReader.new(ApiBucket.bucket.object(id).get.body).read
    end
    

    The spec will be:

       describe '#GET show' do
      let!(:resource) { create(:resource) }
    
      before do
        json_data = Api::V2::Presenter.consume_as_json(resource)
        ResourceController.any_instance.stub(:s3_response).and_return(json_data)
      end
    
      it 'should return the resource in page format' do
        get :show, format: :json, params: { id: resource.uuid }
        response_body_json = JSON.parse(response.body)
        json_data = Api::V2::Presenter.consume_as_json(
            Api::V2::Presenter.new(resource).page,
            true
          )
        expect(response_body_json).to eql(JSON.parse(json_data))
      end
    end