简体   繁体   中英

Getting Rspec unit test coverage with Rails and PostgreSQL

I am trying to write a unit test for the following model concern...

require 'active_support/concern'

module Streamable
  extend ActiveSupport::Concern

  def stream_query_rows(sql_query, options = 'WITH CSV HEADER')
    conn = ActiveRecord::Base.connection.raw_connection
    conn.copy_data("COPY (#{sql_query}) TO STDOUT #{options};") do
      binding.pry
      while row = conn.get_copy_data
        binding.pry
        yield row
      end
    end
  end
end

So far I have battling this with the following spec...

context 'streamable' do
  it 'is present' do
    expect(described_class.respond_to?(:stream_query_rows)).to eq(true)
  end

  context '#stream_query_rows', focus: true do
    let(:sql_query) { 'TESTQRY' }
    let(:sql_query_options) { 'WITH CSV HEADER' }
    let(:raw_connection) do
      Class.new do
        def self.copy_data(args)
          yield
        end

        def self.get_copy_data
          return Proc.new { puts 'TEST' }
        end
      end
    end

    before do
      allow(ActiveRecord::Base).to receive_message_chain(:connection, :raw_connection).and_return(raw_connection)

      described_class.stream_query_rows(sql_query)
    end


    it 'streams data from the db' do
      expect(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};")
    end
  end
end

While I can get the first expect to pass, meaning, I can trigger the first binding.pry, no matter what I try, I can not seem to get past the second.

This is the error...

LocalJumpError:
       no block given (yield)

I am only trying to unit test this and ideally not hit the db, only testing the communication of the objects. This also, can and will be used in many models as an option for streaming data.

Reference article: https://shift.infinite.red/fast-csv-report-generation-with-postgres-in-rails-d444d9b915ab

Does anyone have an pointers on how to finish this stub and or adjust the spec so I have the following block covered?

   while row = conn.get_copy_data
        binding.pry
        yield row
      end

ANSWER

After reviewing the comments and suggestions below, I was able to refactor the spec and now have 100% coverage.

  context '#stream_query_rows' do
    let(:sql_query) { 'TESTQRY' }
    let(:sql_query_options) { 'WITH CSV HEADER' }
    let(:raw_connection) { double('RawConnection') }
    let(:stream_query_rows) do
      described_class.stream_query_rows(sql_query) do
        puts sql_query
        break
      end
    end

    before do
      allow(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};"){ |&block| block.call }
      allow(raw_connection).to receive(:get_copy_data).and_return(sql_query)
      allow(ActiveRecord::Base).to receive_message_chain(:connection, :raw_connection).and_return(raw_connection)
    end

    it 'streams data from the db' do
      expect(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};")
      stream_query_rows
    end

    it 'yields correct data' do
      expect { stream_query_rows }.to output("#{sql_query}\n").to_stdout_from_any_process
    end
  end

Like the error says, you're yield ing, but you haven't supplied a block for it to call.

If your method expects a block, then you need to supply one when you call it.

To do that, you need to change this line:

      described_class.stream_query_rows(sql_query)

to something like this:

      described_class.stream_query_rows(sql_query) { puts "this is a block" }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM