考虑以下ActiveModel::Serializer
类:
class SampleSerializer < ActiveModel::Serializer
attributes :id, :name
end
如何使用 RSpec
进行测试?
考虑以下ActiveModel::Serializer
类:
class SampleSerializer < ActiveModel::Serializer
attributes :id, :name
end
如何使用 RSpec
进行测试?
本答案假设您已经安装并配置了rspec-rails
、active_model_serializers
和factory_girl_rails
三个gem包。
本答案还假设您已经为Sample
资源定义了一个工厂。
在撰写本文时,当前版本(0.10.0.rc3)的active_model_serializers不会接收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'] }
使用 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
@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
例子:您可以以现代风格编写此内容。
分类序列化器:
class CategorySerializer < ActiveModel::Serializer
attributes :id, :name
end
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
let(:serializer) { described_class.new(category, scope: user, scope_name: :current_user) }
。 - Piioosubject { 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
# 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