代码之家  ›  专栏  ›  技术社区  ›  Jamison Dance

用Devise和RSpec测试轨道3中的控制器

  •  2
  • Jamison Dance  · 技术社区  · 15 年前

    我在测试我的控制器 create 行动。我在用 Devise 对于身份验证,并已使用before_filter方法限制对 创造 对已登录用户的操作。这是我的控制器的相关部分。

    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">
    

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

    2 回复  |  直到 15 年前
        1
  •  1
  •   zetetic    15 年前

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

    打电话 as_null_object 本质上告诉mock接受所有没有存根的消息并返回self。所以当你打电话

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

    通常会返回一个新的 RawDataSet 关联到 current_user ,相反,你会得到 当前用户 再一次。

    所以当你试图调用redirect时,传入 @raw_data_set ,你把它当作模拟品而不是 原始数据集 实例,因此 user_url .

    我认为解决这个问题的方法就是使用一个真正的用户(或工厂)并在上面删除设计方法。所以你的 mock_users 变成(例如):

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

    现在 当前用户 将通过关联实际执行生成和保存。

    纯粹主义者会告诉你嘲笑和拒绝一切,直到你脸色发青。去死吧——你还有更好的事情要做。:)

    另一种方法是测试是否接收到生成消息,而不检查保存是否成功。假设您的模型测试将验证通过关联保存是否有效——为什么要在控制器中再次测试?

        2
  •  0
  •   Matilda    14 年前

    如果需要执行特定的操作(例如更改登录行为),可以从设计控制器继承并覆盖它们。 你可以按照这里的指示去做 https://github.com/plataformatec/devise 配置控制器。