Rails 测试从控制器调用的方法
Rails test that method is called from the controller
我有一个调用服务的控制器函数,我想测试是否使用正确的参数调用该服务。
def send_for_signature
client = Client.find(params[:client_id])
external_documents = params[:document_ids].map{|id| ExternalDocument.find(id)}
service = EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values])
result = service.process
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
如何编写测试以确保 EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values])
被正确调用?
你需要的是一个叫做 expecting messages 的东西。
我一般是这样写的:
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:new).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
expect(response.status).to have_http_status(:success)
end
我将从向服务添加工厂方法开始:
class EsignGenieSendByTemplate
# ...
def self.process(**kwargs)
new(**kwargs).process
end
end
这种代码几乎是任何类型服务对象的样板,并在服务对象和它的消费者(如控制器)之间提供更好的API。
此方法应包含在您的服务规范中的示例中。
describe '.process' do
let(:options) do
{ client: 'A', external_documents: 'B', form_values: 'C' }
end
it "forwards its arguments" do
expect(described_class).to recieve(:new).with(**options)
EsignGenieSendByTemplate.process(**options)
end
it "calls process on the instance" do
dbl = instance_double('EsignGenieSendByTemplate')
allow(described_class).to recieve(:new).and_return(dbl)
expect(dbl).to recieve(:process)
EsignGenieSendByTemplate.process(**options)
end
end
你的控制器应该只调用工厂方法而不是实例化 EsignGenieSendByTemplate:
def send_for_signature
client = Client.find(params[:client_id])
# Just pass an array to .find instead of looping - this create a single
# db query instead of n+1
external_documents = ExternalDocument.find(params[:document_ids])
result = EsignGenieSendByTemplate.process(
client: client,
external_documents: external_documents,
form_values: params[:form_values]
)
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
控制器和服务之间更好的 API 让您可以在 EsignGenieSendByTemplate
class 上设置期望,这样您就不必在 expect_any_instance
上胡闹了或存根 .new
方法。
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:process).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
end
我有一个调用服务的控制器函数,我想测试是否使用正确的参数调用该服务。
def send_for_signature
client = Client.find(params[:client_id])
external_documents = params[:document_ids].map{|id| ExternalDocument.find(id)}
service = EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values])
result = service.process
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
如何编写测试以确保 EsignGenieSendByTemplate.new(client: client, external_documents: external_documents, form_values: params[:form_values])
被正确调用?
你需要的是一个叫做 expecting messages 的东西。
我一般是这样写的:
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:new).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
expect(response.status).to have_http_status(:success)
end
我将从向服务添加工厂方法开始:
class EsignGenieSendByTemplate
# ...
def self.process(**kwargs)
new(**kwargs).process
end
end
这种代码几乎是任何类型服务对象的样板,并在服务对象和它的消费者(如控制器)之间提供更好的API。
此方法应包含在您的服务规范中的示例中。
describe '.process' do
let(:options) do
{ client: 'A', external_documents: 'B', form_values: 'C' }
end
it "forwards its arguments" do
expect(described_class).to recieve(:new).with(**options)
EsignGenieSendByTemplate.process(**options)
end
it "calls process on the instance" do
dbl = instance_double('EsignGenieSendByTemplate')
allow(described_class).to recieve(:new).and_return(dbl)
expect(dbl).to recieve(:process)
EsignGenieSendByTemplate.process(**options)
end
end
你的控制器应该只调用工厂方法而不是实例化 EsignGenieSendByTemplate:
def send_for_signature
client = Client.find(params[:client_id])
# Just pass an array to .find instead of looping - this create a single
# db query instead of n+1
external_documents = ExternalDocument.find(params[:document_ids])
result = EsignGenieSendByTemplate.process(
client: client,
external_documents: external_documents,
form_values: params[:form_values]
)
if result["result"] == "success"
head 200
else
render json: result["error_description"], status: :unprocessable_entity
end
end
控制器和服务之间更好的 API 让您可以在 EsignGenieSendByTemplate
class 上设置期望,这样您就不必在 expect_any_instance
上胡闹了或存根 .new
方法。
it 'requests the signature' do
expect(EsignGenieSendByTemplate).to receive(:process).with(client: 'A', external_documents: 'B', form_values: 'C')
get :send_for_signature, params: { ... }
end