【问题标题】:Why is there no user object passed as params for this rails_spec test?为什么没有用户对象作为此 rails_spec 测试的参数传递?
【发布时间】:2019-02-21 05:01:14
【问题描述】:

我有以下 rails_spec 测试:

require 'rails_helper'

RSpec.describe UsersController, type: :controller do
  fixtures :users, :clients
  include AuthHelper
  before do
    @request.headers["Content-Type"] = 'application/json'
    @request.headers["Accept"] = 'application/json'
  end



 describe '#create' do
    context 'for a user with a valid authorization token' do
      context 'having admin privileges' do
        before do
          init_headers_for_admin
        end

        context 'submitted with a valid set of parameters' do
          it 'should create a new user having the submitted parameters' do
            post :create, params: {
                :name => 'Roland Deschain',
                :email => 'roland@foamfactorybrewing.com',
                :role => 'user',
                :username => 'roland'
            }

            expect(response).to have_http_status(:ok)

            user = json

            expect(user).to_not be_nil
            expect(user.name).to eq('Roland Deschain')
            expect(user.email).to eq('roland@foamfactorybrewing.com')
            expect(user.role).to eq('user')
            expect(user.username).to eq('roland')
            expect(user.created_at).to eq(user.updated_at)
          end
        end

我还有以下控制器定义:

def create
# get_required_fields.each { |field|
#   unless params.has_key? field
#     render :json => { :error => 'A ' + field.to_s + ' must be specified in order to create a new User'}, :status => :unprocessable_entity and return
#   end
# }

username = params[:username]
unless User.find_by_username(params[:username]) == nil
  render :json => { :error => I18n.t('error_username_conflict') }, :status => :conflict and return
end

unless User.find_by_email(params[:email]) == nil
  render :json => { :error => I18n.t('error_email_conflict') }, :status => :conflict and return
end
end

但是,当我运行测试时,params 变量始终是一个空哈希。如果我取消注释检测我是否具有所需参数的代码,它总是返回UNPROCESSABLE_ENTITY,而不是NO_CONTENT,如果usernamenameemail 存在,我会期望。我不太清楚为什么,但我似乎无法让它通过post 方法传递实际数据。我一直在看这个,并试图调试它,但我似乎无法让它正确传递参数。

我正在使用 Rails 5 和 rspec_rails 3.8.1。

编辑

这里确实有问题,因为如果我将create 简化为:

  def create
unless params.has_key?('name') && params.has_key?('username') && params.has_key?('email')
  render :json => {:error => I18n.t('error_must_specify_username_email_and_name')}, :status => :unprocessable_entity and return
end

结束

并执行与以前相同的规范(显然希望它返回 no_content,而不是 unprocessable_entity),我最终会发生同样的事情 - 在调试模式下,它看起来像 params 进入变量这个方法是:

{"controller"=>"users", "action"=>"create", "user"=>{}}

所以,首先,它设置了user 参数,这不是我想要的完全,而且,它设置为一个空哈希。我不确定是什么原因造成的。我觉得这是我在 Rails 5.0 中忘记做的事情,因为这种模式在 Rails 4.0 中似乎可以正常工作。

编辑 2

我将测试修改为:

it 'should create a new user record' do
              post :create, params: { user: @user }, as: :json

              expect(response).to have_http_status(:ok)
              expect(json.name).to eq(@user.name)
              expect(json.email).to eq(@user.email)
              expect(json.username).to eq(@user.username)
              expect(json.role).to eq(@user.role)
            end

并将UsersController 类修改为如下所示:

class UsersController < ApplicationController
  include AuthHelper

  before_action :validate_api_key_for_auth, only: :login
  before_action :authenticate_as_admin, only: [:create]

  def create
    p user_params
    unless user_params.has_key?('name') && user_params.has_key?('username') && user_params.has_key?('email')
      render :json => {:error => I18n.t('error_must_specify_username_email_and_name')}, :status => :unprocessable_entity and return
    end
  end

  def user_params
    params[:user].permit([:username, :name, :email, :role]).to_h
  end
end

它正在打印出:{} 用于来自 user_paramsusers 哈希,但我不明白为什么会这样。

编辑 3

authenticate_as_admin 以某种方式搞砸了参数。如果我将authenticate_as_admin 的定义更改为true,它会显示正确的哈希并按预期返回no_contentauthenticate_as_admin函数的定义如下(在ApplicationController中定义):

def authenticate_as_admin
  authenticate_as :admin
end

def authenticate_as(role)
    # First, get the headers for the request
    authorization_header = get_authorization_header

    unless authorization_header
      render :json => {:error => I18n.t('error_must_be_logged_in')}, :status => :forbidden and return false
    end

    auth_token = authorization_header.split[1]

    decoded_token = decode_authorization_header authorization_header
    unless decoded_token
      render :json => {:error => I18n.t('error_invalid_token')}, :status => :forbidden and return false
    end

    payload = decoded_token[0]
    user_id = payload['user_id']
    auth_token_expiration_date = payload['expiration_date']

    @authenticated_user = User.find(user_id) rescue nil

    unless @authenticated_user and @authenticated_user.auth_token == auth_token
      render :json => {:error => I18n.t('error_must_be_logged_in')}, :status => :forbidden and return false
    end

    if auth_token_expiration_date < DateTime.now
      render :json => {:error => I18n.t('error_auth_token_expired')}, :status => :forbidden and return false
    end

    unless @authenticated_user.role_at_least? role
      render :json => {:error => I18n.t('error_insufficient_permission')}, :status => :forbidden and return false
    end

    true
  end

我似乎不明白为什么这会弄乱参数,特别是考虑到这在 Rails 4.2 中运行良好。有人看到我遗漏的任何东西吗?

编辑 4

我觉得我在正确的轨道上。似乎authenticate_as() 函数返回false,我得到了这种行为。如果before_action 返回false,控制器操作的预期结果应该是什么?我希望before_action 中调用的方法会执行适当的render ... 调用,而before_action 会默默地失败,永远不会调用本来会调用的函数。我仍然不太确定函数失败的原因,但我试图了解 应该 发生的行为。也许身份验证检查不是 Rails 5 中 before_actions 的一个很好的用例?

【问题讨论】:

  • 控制器中是否允许使用 params 方法?它有一些require 部分吗?看起来你需要在规格中传递params: { user: { name: ...} }
  • 我尝试添加一个名为user_params 的方法,它允许所有参数(即params.permit!UsersController,以及使用它代替直接参数,但没有运气.
  • 认为这是因为:blog.bigbinary.com/2016/02/13/… 我还不知道如何让before_action 执行render 的错误消息 + json,而不仅仅是在执行throw :abort 时崩溃。
  • 拒绝我的最后评论。情况似乎并非如此。我认为这解决了它,但这只是我疲倦的眼睛在另一次测试中欺骗了我。回到绘图板。

标签: ruby-on-rails ruby parameters


【解决方案1】:

更新不适用于 rails 5

试试这个,

post :create, {
            :name => 'Roland Deschain',
            :email => 'roland@foamfactorybrewing.com',
            :role => 'user',
            :username => 'roland'
        }

当您拥有params: {} 时,它会作为嵌套散列{ params: {...} } 发送

编辑 1

不要将params 传递给post :create,而是尝试在控制器中删除params 调用,类似于

allow(subject).to receive(:params).and_return(params)

在描述块内部,

let(:params) { ActionController::Parameters.new({ :name => '', :email => '' ... }) }

如果您不需要 ActionController::Parameters,则只需一个常规哈希值

并在不带任何参数的情况下调用:create

post :create

看看这是否有帮助,我在另一个项目的规范中使用了这个模式,该项目正在通往 rails 5 的路上。

【讨论】:

  • params: {} 是新 rspec 的有效语法
  • 是的,我使用 params: 这样做的原因是因为我使用的是 rails 5.0,如果没有附加符号,我会收到以下错误:DEPRECATION WARNING: Using positional arguments in functional tests has been deprecated, in favor of keyword arguments, and will be removed in Rails 5.1.
  • @jwir3 嗯,很高兴知道,刚刚将我们的主要代码库移动到 rails 4.2.10,这将很方便转到 rails 5。
  • @subash 由于您更新了对 rails 5 无效的答案,因此我删除了我的反对票。不幸的是,它仍然没有回答我提出的问题。 :( 我觉得 Rails 5 如何处理我没有得到的参数有一些基本的东西。
  • 我可以按照您在答案编辑中的建议进行操作,但我觉得这是在解决真正的问题,即我不明白 rails 在版本 5+ 中是如何传递参数的。我想我需要解决这个问题,否则它会在未来咬我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多