2016-12-09 4 views
0

У меня есть контроллер:Как протестировать контроллер без попадания в базу данных?

class InvoicesController < ApplicationController 
    def edit 
    @invoice = Invoice.includes(:client, :document_status).find(params[:id]) 
    return head :forbidden unless @invoice.editable? 
    end 
end 

Я хочу написать тест, но без удара по базе данных:

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 

Есть ли лучший подход? Мне не нравится эта цепочка allow, но я не могу придумать ничего лучшего. Установка переменной экземпляра @invoice в тесте была бы плохой, потому что тогда я бы опирался на реализацию.

Я мог бы просто создать счет в БД и не беспокоиться обо всех насмешках и заглушках. Однако выполнение этого во всех спецификациях приведет к замедлению всего набора тестов.

+0

Почему не интеграционный тест, чтобы вы могли реализовать весь стек? –

ответ

1

Вы можете переместить часть запроса к объекту запроса так, то вы можете иметь этот код:

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 

, то вы можете легко проверить контроллер, не задев базы данных

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 

Рекомендуется, чтобы проверить InvoiceQuery с базой данных в качестве объекта запроса и выполняет вызовы в базе данных напрямую