Reputation: 3558
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
Upvotes: 0
Views: 183
Reputation: 4435
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" }
Upvotes: 1