Rails devise 内置 controller 测试 (rspec)

hiveer · 2014年06月10日 · 最后由 hiveer 回复于 2014年06月11日 · 3105 次阅读

Rspec 配置

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
  ...

先上路由

devise_for :users, controllers: { passwords: "users/passwords", sessions: "users/sessions", registrations: "users/registrations"}

先说 SessionsController,做了本地化

class Users::SessionsController < Devise::SessionsController    
end

测试代码

require 'spec_helper'

describe Users::SessionsController do

  controller do
    def after_sign_in_path_for(resource)
      super resource
    end
  end

  describe "After user sigin-in" do
    before(:each) do
      @user = FactoryGirl.create(:user)     
    end

    it "change current_user" do
      sign_in @user
      expect( subject.current_user ).to eq(@user)
    end

    it "redirects to the user_root_path" do
      user = FactoryGirl.create(:user)
      controller.after_sign_in_path_for(@user).should == root_path
    end
  end

这个测试没有问题顺利通过! 然后我依葫芦画瓢来做注册测试,也就是 RegistrationsController 的测试,同样的做了本地化,然后测试代码如下

require 'spec_helper'

describe Users::RegistrationsController do

  # before(:each) do
  #   @request.env["devise.mapping"] = Devise.mappings[:user]
  # end

  controller do
    def after_sign_up_path_for(resource)
      super resource
    end
  end

  describe "User sign_up" do  

    it "change current_user" do
      post :create, user: FactoryGirl.attributes_for(:register_user)
      expect( subject.current_user ).not_to be nil
    end

但是这个测试就没法通过,报错如下

1) Users::RegistrationsController User sign_up change current_user
     Failure/Error: post :create, user: FactoryGirl.attributes_for(:register_user)
     AbstractController::ActionNotFound:
       The action 'create' could not be found for AnonymousController
     # ./spec/controllers/registrations_controller_spec.rb:18:in `block (3 levels) in <top (required)>'

各位有什么建议?

controller block 默认是创建 AnonymousController,并且会自动创建 AnonymousController 的路由,当然 action 得你自己写。你写了这个就等于把当前的 controller 彻底改变了。get, post 都会度到 AnoymousController#get, #post 等。

你的第一段测试看似通过,其实并没有真正测到这个功能。sign_in是 Devise 给出的一个 helper, 目的是在测试里面快速 sign in, 通过 Warden 直接实现,不会经过任何路由。

并且第一段测试也没有意义,第一个是测 Devise 已经给出的 sign_in helper, 第二个只是证明 after_sign_in_path 默认 root_path, 和 redirect 完全无关。

怎么改进得你自己决定。controller spec 本质是是 functional testing, 也可以有多种风格,可伸可缩。

如果是偏 unit 的风格,那么在这里完全无需自定义 after_sign_in_path,只要测到 ApplicationController 收到这个 method 就可以结束。然后 ApplicationController 里面单独测这个。

如果是偏 integration 的风格,那么也无需管这个,测 redirect 用 controller.full_path 符合要求就行了。这个就已经包含了 after_sign_in_path 的逻辑。

我个人的做法是,如果没有什么特殊的,测一个 integration 就可以收工了,主要是帮忙看看代码里面有什么 bug。如果逻辑比较特殊,就 ApplicationController 里面单独测。stub controller block, 总之不会花太长时间。

@billy 非常感谢!很犀利,一针见血的指出了我测试代码的逻辑问题,我会改进这个问题。在你说到的 AnonymousController 这块,我最大的疑惑是在 SessionsController 和 RegistrationsController 两次测试的对比,同样的做法(当然这个做法想你说的会存在逻辑的问题,但不至于报错),为什么 RegistrationsController 的测试会出现问题,而 SessionsController 的测试可以通过。

@hiveer 不客气。其实你第一段测试里面的路由也是有问题的,get, post 是达不到的。只是你的 examples 根本没有通过路由,所以看不出来。

@billy 也就是说 SessionsController 因为没有实际经过路由,所以没有把问题体现出来,而 RegistrationsController 真正的产生了实际的调用,所以报错了。那你觉得我应该怎么修改 RegistrationsController 的测试代码才能解决这个问题呢,官方文档的建议是我注释掉的那段代码,但是对这个问题似乎没有帮助。

@billy 再次感谢 Billy 的指正和建议,我在看了 https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller 之后,明白了事情的因果关系!我相信,跟我一样遇到同样问题的新手也会从这个链接得到想要的答案,所以就不在此赘述。

@hiveer 再次不客气。那个 relish 链接里面有两个测试是我交的 PR, 所以比较熟一点,嘿嘿。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号