【问题标题】:Test ActiveModel::Serializer classes with Rspec使用 Rspec 测试 ActiveModel::Serializer 类
【发布时间】:2016-02-13 03:33:16
【问题描述】:

给定以下ActiveModel::Serializer 类:

class SampleSerializer < ActiveModel::Serializer
  attributes :id, :name
end

如何使用RSpec 进行测试?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 rspec active-model-serializers


    【解决方案1】:

    假设

    此答案假定您已安装并配置了 rspec-railsactive_model_serializersfactory_girl_rails gem。

    此答案还假设您已经为 Sample 资源定义了一个工厂。

    序列化器规范

    对于撰写本文时active_model_serializers 的当前版本(0.10.0.rc3),ActiveModel::Serializer 类不接收to_json,而是包装在适配器类中。要获得封装在序列化器实例中的模型的序列化,必须创建一个适配器实例:

    before(:each) do
      # Create an instance of the model
      @sample = FactoryGirl.build(:sample)
    
      # Create a serializer instance
      @serializer = SampleSerializer.new(@sample)
    
      # Create a serialization based on the configured adapter
      @serialization = ActiveModelSerializers::Adapter.create(@serializer)
    end
    

    适配器实例接收to_json方法并返回模型的序列化。

    subject { JSON.parse(@serialization.to_json) }
    

    然后可以对返回的 JSON 运行期望。

    it 'should have a name that matches' do
      expect(subject['name']).to eql(@sample.name)
    end
    

    解析 JSON 响应时,必须考虑适配器配置:

    • 默认配置 :attributes 生成一个没有根键的 JSON 响应:

      subject { JSON.parse(@serialization.to_json) }
      
    • :json 配置根据模型名称生成带有根键的 JSON 响应:

      subject { JSON.parse(@serialization.to_json)['sample'] }
      
    • :json_api 配置生成符合jsonapi 标准的 JSON:

      subject { JSON.parse(@serialization.to_json)['data']['attributes'] }
      

    【讨论】:

    • 这是最棒的。想知道你是否可以为我解开一些东西。我有一个多态序列化程序,在过去的几个小时里,我无法使用您上面概述的步骤来渲染相关记录。如果我直接使用 SomeSerializer.new(resource).associations 我似乎能够看到它们,但它绝不是序列化的数据包。谢谢
    • 注意:ActiveModel::Serializer::Adapter.create 已弃用。使用 ActiveModelSerializers::Adapter.create。
    • 我应该在什么文件中编写测试
    【解决方案2】:

    使用active_model_serializers 时,有一个更简单的方法,只需在序列化程序上调用serializable_hash

    it 'should include a correct name' do
      sample = FactoryBot.create(:sample)
      serializer = SampleSerializer.new(sample)
      expect(serializer.serializable_hash[:name]).to eq 'Heisenberg'
    end
    

    【讨论】:

    • 只是清洁解决方案
    • 但是不关注根节点(类型)
    【解决方案3】:

    @gnerkus 的回答有助于指导我自己的实现,但我选择了不同的方法。测试ActiveModel::Serializer 的返回值,其中序列化程序没有进行额外的处理,似乎是在测试特定键的存在以及ActiveModel::Serializer 是否工作。为了避免测试ActiveModel::Serializer 而是测试特定键是否存在,下面是我测试给定序列化程序的方法:

    describe SampleSerializer do
      subject {  SampleSerializer.new(sample) }
    
      it "includes the expected attributes" do
        expect(subject.attributes.keys).
          to contain_exactly(
            :sample_key,
            :another_sample_key
          )
      end
    
      def sample
        @sample ||= build(:sample)
      end
    end
    

    注意contain_exactly 的使用:这确保除了您指定的键之外没有其他键存在。如果包含意外属性,使用include 将导致测试不会失败。当您更新属性但无法更新测试时,这可以很好地扩展,因为测试会引发错误并迫使您保持所有内容都是最新的。

    仅当您想要测试已添加到给定序列化程序的自定义方法时才测试键的例外情况,在这种情况下,我强烈建议为受该方法影响的返回值编写测试。

    更新

    为了测试关系,您需要对序列化程序进行更多设置。对于简单的序列化程序,我避免使用此设置,但此修改后的设置将帮助您测试链接、关系等是否存在。

    describe SampleSerializer do
      subject do
        ActiveModelSerializers::Adapter.create(sample_serializer)
      end
    
      it "includes the expected attributes" do
        expect(subject_json(subject)["data"]["attributes"].keys).
          to contain_exactly(
            "date"
          )
      end
    
      it "includes the related Resources" do
        expect(subject_json(subject)["data"]["relationships"].keys).
          to contain_exactly(
            "other-resources"
          )
      end
    
      def subject_json(subject)
        JSON.parse(subject.to_json)
      end
    
      def sample_resource
        @sample_resource ||= build(:sample_resource)
      end
    
      def sample_serializer
        @sample_serializer ||=
          SampleSerializer.new(sample_resource)
      end
    end
    

    【讨论】:

    【解决方案4】:

    示例:您可以编写这种现代风格。

    类别序列化器:

    class CategorySerializer < ActiveModel::Serializer
      attributes :id, :name
    end
    

    RSpec:

    require 'rails_helper'
    
    RSpec.describe CategorySerializer, type: :serializer do
      let(:category) { FactoryGirl.build(:category) }
      let(:serializer) { described_class.new(category) }
      let(:serialization) { ActiveModelSerializers::Adapter.create(serializer) }
    
      let(:subject) { JSON.parse(serialization.to_json) }
    
      it 'has an id that matches' do
        expect(subject['id']).to eql(category.id)
      end
    
      it 'has a name that matches' do
        expect(subject['name']).to eql(category.name)
      end  
    end
    

    【讨论】:

    • 嗨@Tanbir Hasan 你能回答我的问题吗stackoverflow.com/questions/50781617/…
    • 在 0.10.10 版本中,current_user 在序列化程序中可用。要让它在测试中工作,您需要添加 let(:serializer) { described_class.new(category, scope: user, scope_name: :current_user) }
    【解决方案5】:

    你可以使用 subject { described_class.new(user).serializable_hash } 创建序列化对象。 下面是我的例子

    用户序列化器:

    # frozen_string_literal: true
    
    class UserSerializer < ApplicationSerializer
      attributes :first_name, :last_name, :verification, :avatar_url, :state, :payin_ability
    end
    

    Rspec

    # frozen_string_literal: true
    
    RSpec.describe UserSerializer, type: :serializer do
      let(:user) { create(:user) }
    
      describe '.serializable_hash' do
        subject { described_class.new(user).serializable_hash }
    
        it { expect(subject).to include(:first_name, :last_name, :verification, :avatar_url, :state, :payin_ability) }
    
        it 'returns correct keys and values' do
          expect(subject).to include(
            first_name: be_a(String),
            last_name: be_a(String),
            verification: be_a(String),
            avatar_url: (be_a(String).or be_nil),
            state: be_kind_of(String),
            payin_ability: (be(true).or be(false)),
          )
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2016-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 2013-07-06
      • 1970-01-01
      相关资源
      最近更新 更多