Rails devise 内置 controller 测试 (rspec)

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

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)>'

各位有什么建议?

共收到 7 条回复

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, 所以比较熟一点,嘿嘿。

@billy 赞!!!

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