在Ruby on Rails测试中,如何从ControllerTest调用控制器中定义的方法?



我有一个名为AdControllerTest的测试类,我使用它来测试AdController。从AdControllerTest,我试图调用在AdController中定义的方法,但我不认为我做得对,我找不到正确的方法来做到这一点。

我的测试代码看起来像这样

test "pctr to final list is correct for pctr policy" do
# Make a CTR list
# Make a selectedAds list
# Check that the CTR list reorders the selectedAds appropriately
response = AdCampaign.search query: {
bool: {
must: [
{ match:  { target_gender: "F" },
match:  { target_country: "KR" } } ]
}}
selectedAds = Array.new(NUMBEROFADS) {Hash.new} 
for i in 1..NUMBEROFADS do
selectedAds[i-1] = response.results.to_a[i-1]
end
testCTR = [0.032521635096847835, 0.03863127908388814, 0.007986670179316374]
finalAds = AdController.pctrToAd(selectedAds: selectedAds, pctr: testCTR)
# Manually order selectedAds by testCTR and compare
comparisonAds = Array.new(NUMBEROFADS) {Hash.new}
comparisonAds[0] = selectedAds[1]
comparisonAds[1] = selectedAds[0]
comparisonAds[2] = selectedAds[2]
assert_equal(finalAds, comparisonAds)
end

在这段代码中,我试图调用finalAds = AdController.pctrToAd(selectedAds: selectedAds, pctr: testCTR)

方法pctrToAd是在AdController中明确定义的。

但是我得到一个错误,像这样:

Error:
AdControllerTest#test_pctr_to_final_list_is_correct_for_pctr_policy:
NoMethodError: undefined method `pctrToAd' for AdController:Class
test/controllers/ad_controller_test.rb:166:in `block in <class:AdControllerTest>'

我不应该在控制器内部调用方法吗?如果不是,我该怎么称呼它?

AdController.pctrToAd正在调用AdController上的方法。假设你想在AdController对象上调用方法。

NoMethodError: undefined method `pctrToAd' for AdController:Class
^^^^^^^^^^^^^^^^^^

假设这是AdControllerTest,控制器对象为@controller

finalAds = @controller.pctrToAd(selectedAds: selectedAds, pctr: testCTR)

你没有。

控制器中唯一应该是public的方法是控制器的actions,当路由器响应HTTP请求时调用。

通过向您的应用程序发送带有集成和系统测试的HTTP请求来测试这些。

编写控制器测试是一种非常有缺陷和过时的方法,不被Rails核心团队推荐。隔离控制器实际上是非常困难的,因为它们是机架应用程序,并且对传入请求和机架中间件堆栈有很强的依赖性。这也不是一个好主意,因为所需的大量mock会让bug溜走。

如果你有一个方法,你想从外部调用它不属于控制器。把它放在模型、服务对象或其他任何容易测试其隔离性的地方。如果你不需要直接调用/test,把它放在一个私有方法中,然后间接地测试它。

控制器已经有很多职责了。不要把它们变成应用程序的垃圾抽屉。

你该怎么做呢?

提取需要单独测试的方法的逻辑。这可以是模型层,也可以是普通的Ruby对象,比如服务对象:

# app/services/ad_campains/frobnobizer_service.rb
module AdCampaigns
class FrobnobizerService
def initialize(selected_ads:, pctr:)
@selected_ads = selected_ads
@pctr = pctr 
end
def call
# do something amazing
end
def self.call(selected_ads:, pctr:)
new(...).run
end
end 
end
require "test_helper"
class AdCampaignsFrobnobizerServiceTest < Minitest::Test
test "fobnobizes the whatchamacallits" do
result = AdCampaigns::FrobnobizerService.call(
selected_ads: [1,2,3], pctr: 'foobarbaz'
)
assert_equals(result[:foo], 'bar')
end
end

当你将它与控制器进行比较时,它非常容易测试,因为它有很少的依赖关系和移动部件,并且只做一项工作。

然后测试控制器通过发送HTTP请求间接调用该组件:

class AdCampaignsController < ApplicationController
# GET /ad_campigns/frobnobize
def frobnobize
@stuff = AdCampaigns::FrobnobizerService.call(
selected_ads: [1,2,3], pctr: 'foobarbaz'
)
# do something with @stuff
end
end
require "test_helper"
class AdCampaignsFlowTest < ActionDispatch::IntegrationTest
test "GET /ad_campigns/frobnobize" do
get '/ad_campigns/frobnobize'
# test that the controller called the service by
# writing assertions/refutions about the headers
# response body or potential side effects.
end
end

在某些情况下,您可能希望使用间谍/模拟来确保控制器按预期调用协作器,而不是测试结果。

最新更新