无法在rspec中使用测试控制器的路由



我是RoR的新手。我正在为一个应用程序编写rspec。我的路线文件包括:

devise_for :sub_users, controllers: {
sessions: 'sub_users/sessions',
passwords: 'sub_users/passwords',
registrations: 'sub_users/registrations',
invitations: 'sub_users/invitations'
}
devise_scope :sub_user do
get '/sub_users/invitation/request/new', to: "sub_users/invitations#new_invitation_request", as: 'new_sub_user_invitation_request'
post '/sub_users/invitation/request/create', to: "sub_users/invitations#create_invitation_request", as: 'create_sub_user_invitation_request'
end

我写rspec如下:

describe 'GET #new_invitation_request' do
subject { post :new_invitation_request, params: params }
let(:params) { { email: 'test@gmail.com'} }
let(:user) { build_stubbed(:user) }
before(:each) do
sign_in(user)
end
it 'should test condition here' do
subject
end
end

但它在subejct上给了我这个错误。我搜索了所有类似的问题,但似乎没有一个能解决我的问题。任何帮助都将不胜感激:

AbstractController::ActionNotFound:
Could not find devise mapping for path "/sub_users/invitation/request/new".
This may happen for two reasons:

1) You forgot to wrap your route inside the scope block. For example:

devise_scope :user do
get "/some/route" => "some_devise_controller"
end

2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:

@request.env["devise.mapping"] = Devise.mappings[:user]

不要使用控制器规格

它们只是ActionDispatch::ControllerTest的一层薄薄的包装,早在2016年就被ActionDispatch::IntegrationTest完全取代了。ActionDispatch::ControllerTest(以及封装它的控制器规范(仍然存在于Rails代码库中,只是为了保持遗留兼容性,但不鼓励使用它。

这种未经处理的方法通过创建一个带有伪造Rack请求的控制器实例来截断框架的大部分(如路由(。请求实际上并没有通过中间件——在Devise的情况下,这是一个致命的缺陷,因为Warden实际上是Rack中间件,负责对用户进行身份验证。

因此,当请求通过路由器中间件(devise_for就是这么做的(时,由于Devise映射没有被插入,控制器就崩溃了。你可以"修复";这个问题是通过包括被弃用的控制器测试助手来解决的,这些助手完全模拟了实际的认证系统——但在这一点上,你实际上在测试什么?

编写请求规范

请求规范看起来有点相似,但发送的实际HTTP请求会通过整个Rails应用程序。它们在现代版本的Rails中同样快(如果不是更快的话(。

# spec/requests/subuser_invitations_spec.rb
RSpec.describe('Subuser Invitations', type: :request) do
let(:user) { create(:user) }
before(:each) do
login_as(user)
end
describe "GET /sub_users/invitations/new" do
subject { get '/sub_users/invitation/request/new' }
it { should be_successful }
end
describe "POST /sub_users/invitations" do
subject { post '/sub_users/invitations', params: { ... } }
it "creates an invitation" do
# just an example...
expect do
post '/sub_users/invitations', params: { ... }
end.to change(Invitation, :count).by(1)
expect(response).to have_http_status :created
end
it "emails the invitation to the user" do
# ...
end
end
end

真正的关键区别在于,控制器规范描述了控制器上的方法,而一个好的请求规范真正描述了应用程序向HTTP公开的端点。

还应该注意的是,在Rails约定中,您从未将showindexcreatedeleteupdate放在路径中。

这是因为HTTP谓词POST、PATCH和DELETE用于确定意图。如果你想要一个你实际应该做什么的例子,看看Devise invitable gem生成的路线。

newedit是该规则的例外,但这是因为它们只是为了显示表单而存在

最新更新