在控制器测试中对模型属性进行拼接

时间:2012-05-11 20:41:16

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

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


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

  

如何在Rails控制器测试中使用模拟和存根来模拟某个状态?


所以我达到了以下内容:

规格

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

2 个答案:

答案 0 :(得分:2)

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

修复测试的最快方法是在post

之前将其添加到测试中
Competition.stub(:find).and_return(competition)

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

我建议你不要在你的工厂里使用那个存根,因为使用工厂的开发人员不知道是什么存在,并且因为这意味着你永远无法测试真实的{{1方法,您需要在questions单元测试以及任何集成测试中执行此操作。

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

答案 1 :(得分:1)

当您在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

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