Étant donné les ActiveModel::Serializer
classe:
class SampleSerializer < ActiveModel::Serializer
attributes :id, :name
end
Comment cela peut-il être testé avec RSpec
?
Cette réponse suppose que vous disposez du rspec-Rails
, active_model_serializers
et factory_girl_Rails
gemmes installées et configurées.
Cette réponse suppose également que vous avez défini une fabrique pour la ressource Sample
.
Pour la version actuelle (0.10.0.rc3) de active_model_serializers au moment de la rédaction, ActiveModel::Serializer
les classes ne reçoivent pas to_json
et sont, à la place, enveloppés dans une classe d'adaptateur. Pour obtenir la sérialisation d'un modèle encapsulé dans une instance de sérialiseur, une instance d'un adaptateur doit être créée:
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
L'instance d'adaptateur reçoit le to_json
et retourne la sérialisation du modèle.
subject { JSON.parse(@serialization.to_json) }
Les attentes peuvent ensuite être exécutées sur le JSON renvoyé.
it 'should have a name that matches' do
expect(subject['name']).to eql(@sample.name)
end
Lors de l'analyse de la réponse JSON, la configuration de l'adaptateur doit être prise en considération:
La configuration par défaut, :attributes
, génère une réponse JSON sans clé racine:
subject { JSON.parse(@serialization.to_json) }
Le :json
config génère une réponse JSON avec une clé racine basée sur le nom du modèle:
subject { JSON.parse(@serialization.to_json)['sample'] }
Le :json_api
config génère un JSON conforme au standard jsonapi :
subject { JSON.parse(@serialization.to_json)['data']['attributes'] }
Lorsque vous utilisez active_model_serializers , il existe un moyen beaucoup plus simple en appelant simplement serializable_hash
sur le sérialiseur:
it 'should include a correct name' do
sample = FactoryBot.create(:sample)
serializer = SampleSerializer.new(sample)
expect(serializer.serializable_hash[:name]).to eq 'Heisenberg'
end
La réponse de @ gnerkus a aidé à guider ma propre implémentation, mais j'ai choisi une approche différente. Test des valeurs renvoyées de ActiveModel::Serializer
où aucun traitement supplémentaire n'est effectué par le sérialiseur semble tester à la fois la présence de clés particulières et si ActiveModel::Serializer
travaille. Pour éviter de tester ActiveModel::Serializer
et testez plutôt la présence de clés spécifiques, voici comment je testerais un sérialiseur donné:
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
Remarquez l'utilisation de contain_exactly
: cela garantit qu'aucune autre clé que celles que vous spécifiez n'est présente. L'utilisation de include
entraînerait l'échec des tests si des attributs inattendus étaient inclus. Cela évolue bien lorsque vous mettez à jour les attributs, mais ne met pas à jour vos tests, car le test générera une erreur et vous forcera à tout mettre à jour.
L'exception aux tests de clés uniquement serait lorsque vous souhaitez tester des méthodes personnalisées que vous avez ajoutées à un sérialiseur donné, auquel cas je recommanderais fortement d'écrire un test pour la ou les valeurs retournées impactées par cette méthode.
Pour tester les relations, vous devrez effectuer un peu plus de configuration avec le sérialiseur. J'évite cette configuration pour les sérialiseurs simples, mais cette configuration modifiée vous aidera à tester la présence de liens, de relations, etc.
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
Exemple: vous pouvez écrire ce style moderne.
Sérialiseur de catégorie:
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