无法使用RSpec测试控制器的POST create操作(使用Devise和CanCan)

6

我在为控制器编写rspec测试时遇到了困难。我想测试POST create操作是否有效。我使用的是rails (3.0.3)、cancan (1.4.1)、devise (1.1.5)和rspec (2.3.0)

模型非常简单

class Account < ActiveRecord::Base
  attr_accessible :name 
end

控制器也是标准的(直接从脚手架生成)。
class AccountsController < ApplicationController
  before_filter :authenticate_user!, :except => [:show, :index]
  load_and_authorize_resource
  ...

  def create
    @account = Account.new(params[:account])

    respond_to do |format|
      if @account.save
        format.html { redirect_to(@account, :notice => 'Account was successfully created.') }
        format.xml  { render :xml => @account, :status => :created, :location => @account }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @account.errors, :status => :unprocessable_entity }
      end
    end
  end

我想要通过的rspec测试是(标题可能不太恰当,请原谅):

 it "should call create on account when POST create is called" do
   @user = Factory.create(:user)
   @user.admin = true
   @user.save

   sign_in @user #this is an admin
   post :create, :account => {"name" => "Jimmy Johnes"}
   response.should be_success
   sign_out @user

 end

然而我得到的只有
AccountsController get index should call create on account when POST create is called
 Failure/Error: response.should be_success
 expected success? to return true, got false
 # ./spec/controllers/accounts_controller_spec.rb:46

其他操作可以进行测试并通过(例如 GET new)

这是 GET new 的测试内容

it "should allow logged in admin to call new on account controller" do
  @user = Factory.create(:user)
  @user.admin=true
  @user.save

  sign_in @user #this is an admin
  get :new
  response.should be_success
  sign_out @user
end

为了完整性,这里提供了能力文件。

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    if user.admin?
      can :manage, :all
    else
      can :read, :all
    end
  end
end

有什么想法吗?我的猜测是我使用了错误的rspec期望,因为代码确实可以工作(只是测试不像预期那样执行!)

2个回答

23

response.should be_success 如果响应代码在200-299的范围内,则返回true。但是create动作会重定向,因此响应代码被设置为302,导致测试失败。

您可以通过使用response.should redirect_to来测试这一点。检查标准RSpec控制器生成器的输出,例如:

  it "redirects to the created account" do
    Account.stub(:new) { mock_account(:save => true) }
    post :create, :account => {}
    response.should redirect_to(account_url(mock_account))
  end

zetetic,你的回答非常好。我不得不进行一些微调才能通过,但其要点是应该进行重定向。 - Dimitris
我将此选为被接受的答案,因为大多数人似乎认为这个答案最有用。 - Dimitris

3

通过zetetic的建议,使测试通过的rspec测试如下:

    it "should call create on account when POST create is called" do
    @user = Factory.create(:user)
    @user.admin = true
    @user.save

    sign_in @user #this is an admin
    account = mock_model(Account, :attributes= => true, :save => true) 
    Account.stub(:new) { account }

    post :create, :account => {}
    response.should redirect_to(account_path(account))
    sign_out @user

end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接