用 Rspec 测试 ActiveModel::Serializer 类
Test ActiveModel::Serializer classes with Rspec
给定以下 ActiveModel::Serializer
class:
class SampleSerializer < ActiveModel::Serializer
attributes :id, :name
end
如何使用 RSpec
进行测试?
假设
此答案假设您已安装和配置 rspec-rails
、active_model_serializers
和 factory_girl_rails
gem。
此答案还假设您已经为 Sample
资源定义了一个工厂。
序列化器规范
在撰写本文时 active_model_serializers 的当前版本 (0.10.0.rc3),ActiveModel::Serializer
classes 不接收 to_json
并且是,相反,包装在适配器 class 中。要获得包装在序列化器实例中的模型的序列化,必须创建一个适配器实例:
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
方法和returns模型的序列化。
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
的返回值,其中 Serializer 没有进行额外的处理,似乎是在测试特定键的存在以及 ActiveModel::Serializer
是否正常工作。为了避免测试 ActiveModel::Serializer
而是测试特定的键是否存在,下面是我将如何测试给定的 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
将导致测试不会失败。当您更新属性但未能更新测试时,这会很好地扩展,因为测试会抛出错误并强制您使所有内容保持最新。
只有当您想测试已添加到给定序列化程序的自定义方法时才会测试键,在这种情况下,我强烈建议为受此影响的返回 value/s 编写测试方法。
更新
为了测试关系,您需要对序列化程序进行更多设置。对于简单的序列化程序,我避免了这种设置,但这种修改后的设置将帮助您测试链接、关系等的存在。
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
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
你可以直接使用
subject { described_class.new(user).serializable_hash }
创建序列化对象。
下面是我的例子
UserSerializer:
# 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
给定以下 ActiveModel::Serializer
class:
class SampleSerializer < ActiveModel::Serializer
attributes :id, :name
end
如何使用 RSpec
进行测试?
假设
此答案假设您已安装和配置 rspec-rails
、active_model_serializers
和 factory_girl_rails
gem。
此答案还假设您已经为 Sample
资源定义了一个工厂。
序列化器规范
在撰写本文时 active_model_serializers 的当前版本 (0.10.0.rc3),ActiveModel::Serializer
classes 不接收 to_json
并且是,相反,包装在适配器 class 中。要获得包装在序列化器实例中的模型的序列化,必须创建一个适配器实例:
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
方法和returns模型的序列化。
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
的返回值,其中 Serializer 没有进行额外的处理,似乎是在测试特定键的存在以及 ActiveModel::Serializer
是否正常工作。为了避免测试 ActiveModel::Serializer
而是测试特定的键是否存在,下面是我将如何测试给定的 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
将导致测试不会失败。当您更新属性但未能更新测试时,这会很好地扩展,因为测试会抛出错误并强制您使所有内容保持最新。
只有当您想测试已添加到给定序列化程序的自定义方法时才会测试键,在这种情况下,我强烈建议为受此影响的返回 value/s 编写测试方法。
更新
为了测试关系,您需要对序列化程序进行更多设置。对于简单的序列化程序,我避免了这种设置,但这种修改后的设置将帮助您测试链接、关系等的存在。
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
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
你可以直接使用
subject { described_class.new(user).serializable_hash }
创建序列化对象。
下面是我的例子
UserSerializer:
# 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