我有一个名为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
在某些情况下,您可能希望使用间谍/模拟来确保控制器按预期调用协作器,而不是测试结果。