我有一个可以属于Project,Task和Subtask的讨论(多态资源)。我在测试以下创建操作时遇到了麻烦:
26 def create
27 @discussion = @parent.discussions.build(params[:discussion])
28 @discussion.user_id = current_user.id
29 if @discussion.save
30 current_user.discussions.push(@discussion)
31 redirect_to [@parent, @discussion], :notice => 'Discussion started'
32 else
33 render 'new'
34 flash.now[:alert] = 'Unable to start discussion'
35 end
36 end
这些是在创建操作之前发生的过滤器之前(这些找到了必要的东西)
63 private
64
65 def find_cached_parent_or_from_something_id
66 @parent || find_parent_from_something_id # this does a bit of caching
67 end
68
69 def find_parent_from_something_id
70 @parent = nil
71 params.each do |name, value|
72 if name =~ /(.+)_id$/
73 @parent = name.humanize.constantize.find(value)
74 end
75 end
76 @parent
77 end
78
79 def find_project_from_parent_belonging_project
80 @project = @parent.belonging_project
81 unless current_user.projects.include?(@project)
82 redirect_to projects_path
83 flash[:alert] = msg_page_not_found
84 end
85 end
如您所见,我可以找到父母和所需的所有变量。现在,我的 RSpec控制器测试创建操作的方式如下:它们全部通过:
104 it "can create discussion on project using valid attributes" do
105 lambda do
106 post :create, :project_id => project,
107 :discussion => valid_attributes
108 flash[:notice].should == 'Discussion started'
109 end.should change(Discussion, :count).by(1)
110 end
111
112 it "can create discussion on task using valid attributes" do
113 lambda do
114 post :create, :task_id => task,
115 :discussion => valid_attributes
116 flash[:notice].should == 'Discussion started'
117 end.should change(Discussion, :count).by(1)
118 end
119
120 it "can create discussion on subtask using valid attributes" do
121 lambda do
122 post :create, :subtask_id => subtask,
123 :discussion => valid_attributes
124 flash[:notice].should == 'Discussion started'
125 end.should change(Discussion, :count).by(1)
126 end
但是,我更愿意测试所有父母的讨论创建尽可能干燥,这与下一次测试有关。
104 ['project', 'task', 'subtask'].each do |parent|
105 it "can create discussion on #{parent} using valid attributes" do
106 lambda do
107 post :create, :parent_id => parent,
108 :discussion => valid_attributes
109 flash[:notice].should == 'Discussion started'
110 end.should change(Discussion, :count).by(1)
111 end
112 end
如何正确编写此测试?
我正在使用FactoryGirl btw。上述测试返回> NameError:未初始化的常量父
另外,如果有更好的方法/做法,请务必纠正我:)
编辑:我已经使用下面接受的答案解决了这个问题,偶然发现了另一个非常相似的问题,我再也不知道......142 it "can access show action for discussion for #{parent}" do
143 get :show, :"#{parent}_id" => self.send(parent),
144 :id => "disucussion_by_another_user_for_#{parent}"
146 response.should be_successful
147 end
这个:id参数写错了...你能帮我写一下吗? (这个discussion_by_another_user_for _#{parent}是我定义的3个不同的工厂......)
这就是我工厂的样子:
16 let!(:discussion_by_another_user_for_project) { FactoryGirl.create(:discussion,
17 :user => another_user,
18 :discussionable => project) }
19 let!(:discussion_by_another_user_for_task) { FactoryGirl.create(:discussion,
20 :user => another_user,
21 :discussionable => task) }
22 let!(:discussion_by_another_user_for_subtask) { FactoryGirl.create(:discussion,
23 :user => another_user,
24 :discussionable => subtask) }
答案 0 :(得分:2)
过去我曾经遇到类似的困境,试图像那样执行“诡计”,但你犯了同样的错误。你应该一次只测试一件事,这样你知道测试失败时出了什么问题。你要做的是在一次测试中测试3种不同的东西,这违背了测试的目的。你应该留下3个“非DRY”测试来测试你正在测试的东西,而不是让1个DRY测试变得混乱。
话虽这么说,你可以试试这样的东西,但是如果它失败你就会遇到必须调试测试和代码的问题,以便知道哪个是错误的之一:
let!(:project) { FactoryGirl.create(:project) }
let!(:task) { FactoryGirl.create(:task) }
let!(:subtask) { FactoryGirl.create(:subtask) }
it "can create discussion on #{parent} using valid attributes" do
parents = [project, task, subtask]
parents.each do |parent|
expect {
post :create, :parent_id => parent,
:discussion => valid_attributes
flash[:notice].should == 'Discussion started'
}.to change(Discussion).by(3)
end
end
注意:如果您使用的是较旧版本的RSpec,请将“expect”更改为“lambda”。
希望这有帮助
答案 1 :(得分:1)
我想你差不多了:
["project", "task", "subtask'].each do |parent|
it "can create discussion on #{parent} using valid attributes" do
lambda do
post :create, :"#{parent}_id" => self.send(parent),
:discussion => valid_attributes
flash[:notice].should == 'Discussion started'
end.should change(Discussion, :count).by(1)
end
end
希望这有帮助。
[编辑]你的额外问题:我不太确定你如何相信传递一个纯粹的字符串实际上会创建一个对象。使用FactoryGirl创建一个对象,如下所示:
FactoryGirl.create(:factory_name)
因此,如果您考虑到这一点,您的测试将成为:
it "can access show action for discussion for #{parent}" do
discussion_by_another_user = FactoryGirl.create("discussion_by_another_user_for_#{parent}".to_sym)
get :show, :"#{parent}_id" => self.send(parent),
:id => discussion_by_another_user.id
response.should be_successful
end
当然,您可以内联参数discussion_by_another_user
,但我认为这更具可读性。希望这会有所帮助。
[编辑]澄清问题后:这实际上完全与您之前提出的相同。 你构建一个字符串,但字符串实际上是你想要调用的方法,和以前一样,你 只写:
it "can access show action for discussion for #{parent}" do
get :show, :"#{parent}_id" => self.send(parent),
:id => self.send("discussion_by_another_user_for_#{parent}")
response.should be_successful
end
HTH