我发现很难在控制器测试中存根模型的某些属性。我想确保尽可能少的存根。
编辑:我已经使用存根进行此类集成。我知道存根不会到达动作调用。现在正确的问题是:
如何在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
。
答案 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
我真的不明白你的控制器应该做什么或者你正在测试什么,抱歉:(