web-dev-qa-db-fra.com

Testez les classes ActiveModel :: Serializer avec Rspec

Étant donné les ActiveModel::Serializer classe:

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

Comment cela peut-il être testé avec RSpec?

30
gnerkus

Hypothèses

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.

Spécifications du sérialiseur

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'] }
    
27
gnerkus

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
18
Jan Klimo

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.

Mise à jour

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
15
Luke Keller

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
6
Engr.Tanbir Hasan