【问题标题】:Stubbing model attributes in controller tests在控制器测试中存根模型属性
【发布时间】:2012-05-11 20:41:15
【问题描述】:

我发现很难在控制器测试中存根模型的某些属性。我想确保尽可能少地存根。


编辑:我已经不再使用存根进行这种集成。我知道存根不会到达动作调用。现在正确的问题是:

如何在 Rails 控制器测试中使用 mocks 和 stub 来模拟某种状态?


所以我达到了以下内容:

规格

require 'spec_helper'

describe TeamsController do
  let(:team) { FactoryGirl.create :team }

  context "having questions" do
    let(:competition) { FactoryGirl.create :competition }

    it "allows a team to enter a competition" do
      post(:enter_competition, id: team.id, competition_id: competition.id)

      assigns(:enroll).team.should == team
      assigns(:enroll).competition.should == competition
    end
  end

  # ...
end

工厂

FactoryGirl.define do
  factory :team do
    name "Ruby team"
  end

  factory :competition, class: Competition do
    name "Competition with questions"

    after_create do |competition|
      competition.
        stub(:questions).
        and_return([ 
          "something"
        ])
    end
  end

  factory :empty_competition, class: Competition do
    name "Competition without questions"
    questions []

    after_create do |competition|
      competition.stub(:questions).and_return []
    end
  end
end

生产代码

class TeamsController < ApplicationController
  def enter_competition
    @team = Team.find params[:id]
    @competition = Competition.find params[:competition_id]
    @enroll = @team.enter_competition @competition

    render :nothing => true
  end
end

class Team < ActiveRecord::Base
  def enter_competition competition
    raise Competition::Closed if competition.questions.empty?

    enroll = Enroll.new team: self, competition: competition
    enroll.save
    enroll
  end
end

当我运行测试时,questions 属性为nil,因此在检查nil.empty? 时模型中的测试失败。

为什么不使用存根以便正确使用该消息的状态?我原以为@competition.questions 会是[ "question" ],但我却得到了nil

【问题讨论】:

    标签: ruby-on-rails rspec factory-bot stub


    【解决方案1】:

    您遇到的问题是stub 在Ruby 对象的实例上工作;它不会影响代表同一行的所有 ActiveRecord 对象。

    修复测试的最快方法是将其添加到测试中,在 post 之前:

    Competition.stub(:find).and_return(competition)
    

    这是必要的原因是Competition.find 将返回一个新的Competition 对象,该对象没有删除questions,即使它代表相同的数据库行。存根find 也意味着它将返回相同的Competition 实例,这意味着控制器将看到存根questions

    不过,我建议不要在您的工厂中使用该存根,因为作为开发人员使用工厂时存根的内容并不明显,而且这意味着您将永远无法测试真正的 questions方法,您需要在 Competition 单元测试以及任何集成测试中执行此操作。

    长话短说:如果您在模型实例上存根一个方法,您还需要为该模型存根 find(或您用来查找的任何类方法)它),但在工厂定义中包含这样的存根并不是一个好主意。

    【讨论】:

      【解决方案2】:

      当您在 FactoryGirl 上调用 create 时,它会创建数据库记录,然后您可以在控制器代码中检索这些记录。所以你得到的实例(@team@competition)是纯 ActiveRecord,没有任何方法被存根。

      我个人会这样写你的测试(根本不接触数据库):

      let(:team) { mock_model(Team) }
      let(:competition) { mock_model(Competition) }
      
      before do
        Team.stub(:find) { team }
        Competition.stub(:find) { competition }
      end
      

      然后在你的测试中是这样的:

      it "should call enter_competition on @team with @competition" do
        team.should_receive(:enter_competition).with(competition)
      
        post :enter_competition, id: 7, competition_id: 10
      

      我真的不明白你的控制器应该做什么或者你在测试什么,抱歉:(

      【讨论】:

      • 感谢您的回答,艺术。然而,Joe 的回答达到了我想要的目的,他还对事情的运作方式进行了解释。
      猜你喜欢
      • 2017-01-04
      • 2013-08-04
      • 1970-01-01
      • 2015-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多