让我们先来看一个例子,假设我们要测试这个action:
def comment
@post = Post.find(params[:id])
@comment = Comment.new(params[:comment])
respond_to do |format|
if @comment.save && @post.comments.concat(@comment)
flash[:notice] = 'Comment was successfully updated.'
format.html { redirect_to post_path(@blogger, @post) }
format.xml { head
k }
else
flash[:notice] = 'Error saving comment.'
format.html { redirect_to post_path(@blogger, @post) }
format.xml { render
ml => @comment.errors.to_xml }
end
end
end
我需要确保我能够对帖子发布评论:
it "should add comments to the post" do
post = posts(:one)
comment_count = post.comments.size
post :comment, :id => 1, :comment => {:title => 'test', :body => 'very nice!'}
post.reload
post.comments.size.should == comment_count + 1
flash[:notice].should match(/success/i)
response.should redirect_to(posts_path(@blogger, @post))
end
看起来不错?但这个测试例实际测试了许多不该它来测试的东西,它应该只测试action,但是它却连同model也一并测试了,并且它还依赖于fixtures。
我们可以使用一个mock object来代替真是的post对象:
post = mock_model(Post)
现在我们需要关注comment的第一行:
@post = Post.find(params[:id])
我希望这一行在测试时得到执行,但是不用测试,我也知道这行代码肯定是工作正常的,因此,我现在要做的就是让这行代码返回我在上面创建的那个mock object:
Post.should_receive(:find).and_return(post)
或者这样:
Post.stub!(:find).and_return(post)
但 是请注意,should_receive和stub!虽然都可以让Post.find返回我的mock object,但是他们的区别却是巨大的,如果我调用了stub!,但是却没有调用Post.find,stub!不会抱怨什么,因为它是个老实人,但是 should_receive就不用,如果我告诉should_receive我会调用Post.find,但却没调用,那问题就来了,它会抱怨为何我不 守信用,并抛出一个异常来警告我。
但是跟在他们后面的and_return的作用却是完全相同的,它让你mock或者stub的方法返回你传递给它的任何参数。
接下来,我需要对comment做同样的事情:
comment = mock_model(Comment)
Comment.should_receive(:new).with({:title => 'comment title', :body => 'comment body'}).and_return(comment)
接下来我需要关注这条语句:
if @comment.save && @post.comments.concat(@comment)
同上面一样:
comment.should_receive(:save).and_return(true)
post.comments.should_receive(:concat).and_return(comment)
下面我需要测试flash及redirect:
flash[:notice].should match(/success/i)
response.should redirect_to(posts_path(blogger, post))
现在,还剩下最后一件事情:这个action必须使用blogger变量来进行重定向,这里我使用instance_variable_set来完成这个工作:
blogger = mock_model(User)
@controller.instance_variable_set(:@blogger, blogger)
基本就是这样,下面是将部分代码放入before中的完整代码:
describe PostsController, "comment", :behaviour_type => :controller, do
before do
@post = mock_model(Post)
Post.should_receive(:find).and_return(@post)
@comment = mock_model(Comment)
Comment.should_receive(:new).with.({:title => 'comment title', :body => 'comment body'}).and_return(@comment)
@blogger = mock_model(User)
@controller.instance_variable_set(:@blogger, @blogger)
end
it "should add comments to the post" do
@comment.should_receive(:save).and_return(true)
@post.comments.should_receive(:concat).and_return(@comment)
post :comment, :id => @post.id, :comment => {:title => 'comment title', :body => 'comment body'}
flash[:notice].should match(/success/i)
response.should redirect_to(posts_path(@blogger, @post))
end
end
相关帖子:

