【问题标题】:How to test Controller post :create of JSON api on rails using rspec?如何测试控制器帖子:使用 rspec 在 Rails 上创建 JSON api?
【发布时间】:2017-09-11 18:31:09
【问题描述】:

为了通过考试,我一直在扯头发。我有一个如下所示的 JSON API:

{
  "data": [
    {
      "id": "b99f8173-0492-457f-9de9-6c1d8d6832ed",
      "type": "manufacturer_organizations",
      "attributes": {
        "account_number": "random test 123"
      },
      "relationships": {
        "organization": {
          "data": {
            "id": "fb20ddc9-a3ee-47c3-bdd2-f710541ff89c",
            "type": "organizations"
          }
        },
        "manufacturer": {
          "data": {
            "id": "1",
            "type": "manufacturers"
          }
        }
      }
    },...

我正在尝试在 Rails 中进行post :create 测试。

let!(:manufacturer_organization) {FactoryGirl.create(:manufacturer_organization)}
let(:manufacturer_organization2) { FactoryGirl.create(:manufacturer_organization)}

...

  describe "POST create" do
    it "posts a valid manufacturer organization data" do
      authenticate
      organization = FactoryGirl.create(:organization)
      manufacturer = FactoryGirl.create(:manufacturer)

      post :create, manufacturer_organization2.to_json #what should I put here instead?

      expect(json['data'].length).to eq(2)
    end

  #=> error: JSON::ParserError: A JSON text must at least contain two octets!

我尝试过各种 SO 帖子(this)thisthis article

以下是我尝试过的一些尝试:

post :create, params: {organization_id: organization.id, manufacturer: manufacturer.id, account_number: "123 test account number"}
#=> error: JSON::ParserError:
   A JSON text must at least contain two octets!

post :create, params: :manufacturer_organization_2
#=> 
 NoMethodError:
   undefined method `symbolize_keys' for :manufacturer_organization_2:Symbol

json = { :format => 'json', :manufacturer_organization => { :account_number => "foo123", :organization_id => organization.id, :manufacturer_id => manufacturer.id } }
post :create, json
#=>  NoMethodError:
   undefined method `length' for nil:NilClass 

如何测试我的控制器通过post :create 接受manufacturer_id, organization_id, and account_number?现在我测试它的方式是计算初始json['data'].length(初始为1);最后,我希望 json['data'].length 在成功 post :create 后为 2。如何模拟创建有效的manufacturer_organization 输入?

编辑:

抱歉,忘记放我的json方法助手了:

def json
  JSON.parse(response.body)
end

另外,这个pass:

  describe "POST create" do
    it "posts a valid manufacturer organization data" do
      authenticate
      organization = FactoryGirl.create(:organization)
      manufacturer = FactoryGirl.create(:manufacturer)
      post :create, {account_number: "Test account numba", organization_id: organization.id, manufacturer_id: manufacturer.id}
      expect(response).to be_success
    end

添加expect(json['success']).to eq("Yay!") 时出现此错误:

JSON::ParserError: A JSON text must at least contain two octets!

控制器:

  def create
    @manufacturer_organization = ManufacturerOrganization.new(manufacturer_organization_params)
    if @manufacturer_organization.save
      puts "success!"
      render json: {success: "Yay!"}
    else
      puts "Sorry, something went wrong!"
    end
  end


def manufacturer_organization_params
    api_params.permit(:organization_id, :manufacturer_id, :account_number)
end

@api_params ||= ActionController::Parameters.new(ActiveModelSerializers::Deserialization.jsonapi_parse(params))

【问题讨论】:

  • 您不需要在规范中明确地将参数转换为 JSON。但是这里的代码中还有很多其他问题。您的示例 JSON 中的有效负载类似于 JSONAPI.org。但它不是创建资源的正确有效负载。此外,您正在使用FactoryGirl.create,这会给您带来唯一性验证问题。而不是在您的控制器中使用puts,而是返回有意义的 HTTP 响应代码并在您的规范中进行测试。
  • 您应该编辑问题并说明您的控制器接受什么以及您尝试测试的结果是什么。这样,回答者就可以给出实际有效的示例,而不是用代码修复 10 个问题中的 1 个。
  • 感谢您的评论,@max!您介意详细说明如何返回有意义的 HTTP 响应代码并在我的规范中进行测试吗?我还编辑了问题并为我的参数添加了更多代码。

标签: ruby-on-rails rspec json-api


【解决方案1】:

在 RSpec 中,您永远*不需要显式格式化参数。

post :create, params: { foo: 'bar' }, format: :json

这将在请求正文中正确地将哈希 { foo: 'bar' } 格式化为 JSON。

要创建与 JSONAPI.org 结构匹配的哈希,您可以创建一个助手:

# support/api_spec_helper.rb
module APISpecHelper
  def to_json_api(model)
    {
      data: {
        type: ActiveModel::Naming.plural(model),
        attributes: model.attributes
      }.tap do |hash| 
        hash[:id] = model.id if model.persisted?
      end
    }
  end
end

您还可以使用 JSONAPI-RB gem 或 ActiveModel::Serializers 来构造/解构 JSONAPI 响应/参数。


require 'rails_helper'
require 'api_spec_helper'

RSpec.request "Manufacturer organizations" do 

  include APISpecHelper

  describe "POST '/manufacturer_organizations'" do
    let(:valid_params) do
      to_json_api(FactoryGirl.build(:manufacturer_organization))
    end
    let(:invalid_params) do
      to_json_api(ManufacturerOrganization.new(
        foo: 'bad_value'
      ))
    end

    describe "with valid attributes" do
      it "creates a manufacturer organization" do
        expect do
          post '/manufacturer_organizations', params: valid_params, format: :json
        end.to change(ManufacturerOrganization, :count).by(+1)
      end

      it "has the correct response" do
        post '/manufacturer_organizations', params: valid_params, format: :json
        expect(response).to have_status :created
        expect(response.headers[:location]).to eq(
          manufacturer_organization_path(ManufacturerOrganization.last)
        )
      end
    end

    describe "with valid attributes" do
      it "does not create a manufacturer organization" do
        expect do
          post '/manufacturer_organizations', params: invalid_params, format: :json
        end.to_not change(ManufacturerOrganization, :count)
      end

      it "has the correct response" do
        post '/manufacturer_organizations', params: invalid_params, format: :json
        expect(response).to have_status :unproccessable_entity
      end
    end
  end
end

返回正确的状态码。

返回正确的响应代码非常简单:

def create
  @resource = Resource.create(some_params)
  if @resource.save
    # you can respond by pointing at the newly created resource but with no body
    head :created, location: @resource
    # or 
    render json: @resource, 
           status: :created, 
           location: @resource
  else
    render json: { errors: @resource.errors.full_messages }, 
           status: :unprocessable_entity
  end
end

如果 POST 请求不包含客户端生成的 ID 和 请求的资源已成功创建,服务器必须 返回 201 Created 状态码。
http://jsonapi.org/format/#crud

其他回复
服务器可以使用其他 HTTP 状态代码进行响应。
服务器可以包含带有错误响应的错误详细信息。

普遍接受的做法是使用422 - Unprocessable Entity 来处理验证错误。

一个小问题是您应该使用 a serializer 来提供正确的 JSON 响应并序列化 the correct error objects

【讨论】:

  • 我更新了这个答案以代表 2018 年更先进的技术,因为自 2015 年以来发生了很大变化。有关如何构造/解构 JSON 和如何处理参数,
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多