【问题标题】:Testing controllers in Rails 3 with Devise and RSpec在 Rails 3 中使用 Devise 和 RSpec 测试控制器
【发布时间】:2010-11-18 06:55:58
【问题描述】:

我正在尝试测试我的控制器的 create 操作。我使用Devise 进行身份验证,并使用了 before_filter 方法来限制登录用户对create 操作的访问。这是我的控制器的相关部分。

class RawDataSetsController < ApplicationController
  before_filter :authenticate_user!, :except => [:show, :index, :download]

  def create
    @raw_data_set = current_user.raw_data_sets.build(params[:raw_data_set])
    if @raw_data_set.save
      redirect_to @raw_data_set
    else
      render "new"
    end
  end
end

在我的测试用例中,我想确保登录用户可以创建 RawDataSet。我认为我已经按照this 博文上的说明模拟了一个经过身份验证的用户。

require 'spec_helper'
describe RawDataSetsController do

  include Devise::TestHelpers

  def mock_users(stubs={})
    @user ||= mock_model(User, stubs).as_null_object
  end

  def log_in_test_user
    attr = { :username => "Foobar", :email => "doineedit@foobar.com" }
    #mock up an authentication in warden as per http://www.michaelharrison.ws/weblog/?p=349
    request.env['warden'] = mock(Warden, :authenticate => mock_users(attr),
                                         :authenticate! => mock_users(attr),
                                         :authenticate? => mock_users(attr))
  end

  before do
    @rds = Factory(:raw_data_set)
  end

  describe "POST 'create'" do

    before do
      @attr = { 
        :organism_name => "Beef Jerky",
        :mass_spec_type => "My Stomach",
      }
    end

    describe "logged in user" do
      it "should create a raw_data_set when the user is logged in" do
        log_in_test_user
        lambda do
          post :create, :raw_data_set => @attr
        end.should change(RawDataSet, :count).by(1)
      end
    end
  end
end

运行此测试用例会导致以下错误:

1) RawDataSetsController POST 'create' logged in user should create a raw_data_set when the user is logged in
 Failure/Error: post :create, :raw_data_set => @attr
 undefined method `user_url' for #<RawDataSetsController:0x0000010175af88>
 # ./app/controllers/raw_data_sets_controller.rb:7:in `create'

我对这个错误感到困惑。经过进一步检查,@raw_data_set 不是 RawDataSet 模型类的实例,而是用户?这是我p @raw_data_set时的样子

#<User:0x807a06a4 @name="User_1002">

这到底是怎么回事?我究竟做错了什么?如何使用经过身份验证的用户测试我的控制器上的创建操作?

【问题讨论】:

    标签: ruby-on-rails-3 rspec devise


    【解决方案1】:

    编辑 (完全错误的第一次尝试已删除)

    调用as_null_object 本质上是告诉模拟程序接受所有没有存根的消息并返回self。所以当你打电话时

    current_user.raw_data_sets.build(params[:raw_data_set])

    这通常会返回与current_user 关联的新RawDataSet,而不是您再次获得current_user

    因此,当您尝试调用重定向时,传入@raw_data_set,您传递的是模拟而不是RawDataSet 实例,因此是对user_url 的错误调用。

    我认为处理这个问题的方法是使用一个真正的用户(或工厂)并将其上的设计方法存根。所以你的mock_users 变成(例如):

      def mock_users(stubs={})
        @user = User.create(stubs)
      end
    

    现在current_user 实际上会通过关联进行构建和保存。

    纯粹主义者会告诉你要嘲笑和剔除一切,直到你脸色发青。去他妈的 - 你有更好的事情要做。 :)

    解决此问题的另一种方法是测试是否收到了构建消息,而不检查保存是否成功。大概您的模型测试将验证通过关联保存是否有效——为什么要在控制器中再次测试?

    【讨论】:

    • 问题是控制器的这一行:@raw_data_set = current_user.raw_data_sets.build(params[:raw_data_set]) 在我的测试中返回了 User 类的实例,这根本不是我想要它做什么。它应该是 RawDataSet 类的一个实例,并重定向到 RawDataSets#show。
    • 感谢您的帮助。我仍然掌握 RSpec 的窍门,所以这很有帮助。
    【解决方案2】:

    如果您需要做一些特定的事情,例如更改登录行为,您可以从设计控制器继承并覆盖它们。 您可以按照此处的说明进行操作 https://github.com/plataformatec/devise 配置控制器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多