0

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


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

如何在 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.

4

2 回答 2

3

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

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

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

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

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

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

于 2012-05-14T13:36:40.407 回答
1

当您调用createFactoryGirl 时,它会创建数据库记录,然后您可以在控制器代码中检索这些记录。所以你得到的实例 ( @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

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

于 2012-05-12T07:49:21.833 回答