gpt4 book ai didi

ruby-on-rails - rspec 中的模拟 Controller 方法

转载 作者:行者123 更新时间:2023-12-01 08:28:31 25 4
gpt4 key购买 nike

(这个问题类似于 Ruby on Rails Method Mocks in the Controller ,但它使用的是旧的 stub 语法,此外,没有收到有效的答案。)

简写

我想测试我的 Controller 代码与我的模型代码分开。不应该是 rspec 代码:

expect(real_time_device).to receive(:sync_readings)

验证 RealTimeDevice#sync_readings 被调用,但禁止实际调用?

细节

我的 Controller 有一个调用 RealTimeDevice#sync_readings 的 #refresh 方法:
# app/controllers/real_time_devices_controller.rb
class RealTimeDevicesController < ApplicationController
before_action :set_real_time_device, only: [:show, :refresh]
<snip>
def refresh
@real_time_device.sync_readings
redirect_to :back
end
<snip>
end

在我的 Controller 测试中,我想验证 (a) @real_time_device 正在设置和 (b) #sync_reading 模型方法被调用(但我不想调用模型方法本身,因为它包含在模型单元测试)。

这是我的 controller_spec 代码不起作用:
# file: spec/controllers/real_time_devices_controller_spec.rb
require 'rails_helper'
<snip>

describe "PUT refresh" do
it "assigns the requested real_time_device as @real_time_device" do
real_time_device = RealTimeDevice.create! valid_attributes
expect(real_time_device).to receive(:sync_readings)
put :refresh, {:id => real_time_device.to_param}, valid_session
expect(assigns(:real_time_device)).to eq(real_time_device)
end
end

<snip>

当我运行测试时,实际的 RealTimeDevice#sync_readings 方法被调用,即它试图调用我模型中的代码。我认为这一行:
        expect(real_time_device).to receive(:sync_readings)

对 stub 方法并验证它是否被调用是必要且足够的。我的怀疑是它需要是双重的。但是我也看不到如何使用 double 编写测试。

我错过了什么?

最佳答案

您正在对 RealTimeDevice 的特定实例设置期望。 Controller 从数据库中获取记录,但在您的 Controller 中,它使用 RealTimeDevice 的另一个实例,而不是您设置期望的实际对象。

这个问题有两种解决方案。

快速和肮脏

您可以对 的任何 实例设置期望值:

expect_any_instance_of(RealTimeDevice).to receive(:sync_readings)

请注意,这不是编写规范的最佳方式。毕竟,这并不能保证您的 Controller 从数据库中获取正确的记录。

模拟方法

第二种解决方案涉及更多的工作,但会导致您的 Controller 被隔离测试(如果它正在获取实际的数据库记录,则不是真的):
describe 'PUT refresh' do
let(:real_time_device) { instance_double(RealTimeDevice) }

it 'assigns the requested real_time_device as @real_time_device' do
expect(RealTimeDevice).to receive(:find).with('1').and_return(real_time_device)
expect(real_time_device).to receive(:sync_readings)

put :refresh, {:id => '1'}, valid_session

expect(assigns(:real_time_device)).to eq(real_time_device)
end
end

相当多的事情已经发生了变化。这是发生的事情:
let(:real_time_device) { instance_double(RealTimeDevice) }

始终更喜欢在规范中使用 RealTimeDevice 而不是创建局部变量或实例变量。 let 允许您延迟评估对象,它不会在您的规范要求之前创建。
expect(RealTimeDevice).to receive(:find).with('1').and_return(real_time_device)

数据库查找已被 stub 。我们告诉 rSpec 确保 Controller 从数据库中获取正确的记录。重要的部分是这里返回了在规范中创建的测试替身的实例。
expect(real_time_device).to receive(:sync_readings)

由于 Controller 现在使用的是测试替身而不是实际记录,因此您可以对测试替身本身设置期望值。

我使用了 rSpec 3 的 let ,它验证了 instance_double 方法实际上是由底层类型实现的。这可以防止规范在缺少方法时通过。 Read more about verifying doubles in the rSpec documentation

请注意,根本不需要在实际 sync_readings 对象上使用测试替身,但它确实使规范更快。 Controller 现在也在完全隔离的情况下进行测试。

关于ruby-on-rails - rspec 中的模拟 Controller 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27143598/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com