【问题标题】:Testing a private method of a rails controller测试 Rails 控制器的私有方法
【发布时间】:2016-05-12 05:27:43
【问题描述】:

我目前正在一个需要 100% 代码覆盖率的团队工作,我终生无法使用这种单行方法来将当前的覆盖率提高到 100%。

我有一个看起来像这样的基本控制器,它从多个其他控制器扩展而来。

module Owners
  module Pets
    class BaseController < Owners::ApplicationController
      private

      def current_pet
        current_owner.all_pets.find(params[:pet_id])
      end
    end
  end
end

我对这个控制器的规格如下所示。

require 'rails_helper'

Rails.describe Owners::Pets::BaseController, type: :controller do
  routes { Owners::Engine.routes }

  controller Owners::Pets::BaseController do
    def index
      current_pet
    end
  end

  let(:user) { double :user, id: 1, owner: nil }

  before { sign_in(user) }

  before do
    allow(request.env['warden']).to receive(:user).and_return user
    allow(Owners::User).to receive(:find).with(user.id).and_return user
  end

  context 'with current owner and pet' do
    let(:owner) { create :owner }
    let(:pet) { create(:owner_pet, owner: owner) }

    describe '#current_pet' do
      before do
        allow(controller).to receive(:current_pet).and_return pet
        routes.append { get 'index' => 'owners/pets/base#index' }
      end

      it do
        get :index
        is_expected.to eq pet
      end
    end
  end
end

规范失败并出现错误“No route matches {:action=>"index", :controller=>"owners/pets/base"}”Routes.append 应该添加正确的路由,对吗?

更新:通过将我的 routes { Owners::Engine.routes } 行移到匿名控制器上方,它不再引发与路由相关的错误。但是现在它将pet 与实际的控制器类进行比较。输出太长,无法粘贴到此处,但本质上是:

expected: #<Owners::Pet>
got: #<#<Class:0x007fc65c860518>:0x007fc65f83a248>

拥有一大堆属性和方法。

【问题讨论】:

  • 这实际上是一种非常有创意的测试私有方法的方法。我有一个建议:您可能必须在response 上断言,因为is_expected.to-syntax 在subject 上断言,这似乎是控制器。我不完全确定当您返回 AR 对象时响应会是什么样子,不过您可能可以通过调试器弄清楚。
  • 类似问题here

标签: ruby-on-rails ruby-on-rails-4 rspec rspec-rails


【解决方案1】:

这个测试没有价值。您正在对您正在测试的方法进行存根。即使#current_pet的方法体抛出异常,测试还是会通过。

一般来说,最好避免直接测试私有方法。您应该能够通过继承自 Owners::Pets::BaseController 的类之一间接测试此方法。

【讨论】:

    【解决方案2】:

    当您使用语法it { is_expected.to ... } 时,Rspec 必须根据测试本身推断“它”是什么。 subject 方法可用于明确指定“它”是什么;在subject 不存在的情况下,Rspec 将实例化正在测试的类的新实例。在您的情况下,这将是控制器本身。

    尝试在#current_pet 块的上下文中显式设置subject

    例如,

    context 'with current owner and pet' do
       let(:owner) { create :owner }
       let(:pet) { create(:owner_pet, owner: owner) }
    
       describe '#current_pet' do
         before do
           allow(controller).to receive(:current_pet).and_return pet
           routes.append { get 'index' => 'owners/pets/base#index' }
         end
    
         # set this to whatever you want "is_expected.to" to be
         subject { controller.current_pet }
    
         it do
           get :index
           is_expected.to eq pet
         end
       end
     end
    

    必读:我必须同意其他发帖者的观点,即这个测试不是很有用。传统观点是只测试公共方法(私有方法通过在公共方法中的使用来测试)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多