Reputation: 4888
I have a controller:
class InvoicesController < ApplicationController
def edit
@invoice = Invoice.includes(:client, :document_status).find(params[:id])
return head :forbidden unless @invoice.editable?
end
end
I want to write a test but without hitting the database:
describe InvoicesController do
describe '#edit' do
let(:invoice_id) { '1' }
let(:invoice) { double(Invoice, editable?: false) }
let(:invoice_includes) { double }
before do
allow(invoice_includes).to receive(:find).with(invoice_id) { invoice }
allow(Invoice).to receive(:includes).with(:client, :document_status) { invoice_includes }
end
subject { get :edit, params: {id: invoice_id} }
it { is_expected.to have_http_status(403) }
end
end
Is there a better approach? I don't like this chain of allow
s but I can't think of anything better. Setting the @invoice
instance variable in the test would be bad, because then I would rely on the implementation.
I could obviously just create the invoice in the DB and don't bother about all the mocks and stubs. However, doing this in all the specs would slow down the whole test suite.
Upvotes: 0
Views: 109
Reputation: 754
You can move the query part to the query object so then you can have this code:
class InvoiceQuery
def find(id)
::Invoice.includes(:client, :document_status).find(id)
end
end
class InvoicesController < ApplicationController
def edit
@invoice = invoice_query.find(params[:id])
return head :forbidden unless @invoice.editable?
end
private
def invoice_query
::InvoiceQuery.new
end
end
then you can easily test your controller without hitting database
describe InvoicesController do
describe '#edit' do
let(:invoice_query) do
instance_double(InvoiceQuery, find: double)
end
let(:invoice)
instance_double(Invoice, editable?: false)
end
let(:invoice_id) { '1' }
before do
allow(InvoiceQuery).to receive(:new).and_return(invoice_query)
allow(invoice_query).to receive(:find).with(invoice_id).and_return(invoice)
end
subject { get :edit, params: {id: invoice_id} }
it { is_expected.to have_http_status(403) }
end
end
Is recommended to test InvoiceQuery
with database as it's query object and it performs calls to database directly
Upvotes: 1