133 votes

Comment tester une préoccupation dans Rails

Étant donné que j'ai un Personable dans mon application Rails 4 qui a une full_name Comment puis-je tester cette méthode à l'aide de RSpec ?

concerns/personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

0 votes

Quel cadre de test utilisez-vous ? N'oubliez pas non plus que Personable n'est qu'un module Ruby normal. Testez-le comme vous le feriez pour n'importe quel autre mixin.

0 votes

N'a pas ActiveSupport::Concern a été retiré de Rails ? Je pensais que ça avait disparu il y a un petit moment.

0 votes

@LeeJarvis J'utilise Rspec et FactoryGirl.

218voto

Josh Leitzel Points 7212

La méthode que vous avez trouvée fonctionnera certainement pour tester un peu de fonctionnalité mais semble assez fragile - votre classe factice (en fait juste une Struct dans votre solution) peut ou non se comporter comme une vraie classe qui include est votre préoccupation. De plus, si vous essayez de tester les préoccupations du modèle, vous ne pourrez pas faire des choses comme tester la validité des objets ou invoquer des callbacks ActiveRecord à moins de configurer la base de données en conséquence (parce que votre classe factice n'aura pas de table de base de données pour la soutenir). De plus, vous voudrez non seulement tester la préoccupation, mais aussi tester le comportement de la préoccupation dans vos spécifications de modèle.

Alors pourquoi ne pas faire d'une pierre deux coups ? En utilisant l'outil RSpec groupes d'exemples partagés vous pouvez tester vos préoccupations par rapport aux classes qui les utilisent (par exemple, les modèles). et vous pourrez les tester partout où ils sont utilisés. Et vous n'avez qu'à écrire les tests une seule fois, puis à les inclure dans toute spécification de modèle qui utilise votre préoccupation. Dans votre cas, cela pourrait ressembler à quelque chose comme ceci :

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

Les avantages de cette approche deviennent encore plus évidents lorsque vous commencez à faire des choses dans votre préoccupation comme invoquer des callbacks AR, où tout ce qui n'est pas un objet AR ne fera pas l'affaire.

2 votes

L'inconvénient de cette méthode est qu'elle ralentit parallel_tests . Je pense qu'il sera préférable d'avoir des tests séparés au lieu d'utiliser shared_examples_for y it_behaves_like .

9 votes

@ArtemKalinchuk Je ne suis pas sûr que ce soit vrai, par github.com/grosser/parallel_tests/issues/168 parallel_tests sont basés par fichier, donc les exemples partagés ne devraient pas le ralentir. Je dirais également que les comportements partagés correctement groupés l'emportent sur la vitesse de test.

8 votes

Veillez à inclure le concerns dans votre spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871

75voto

kyledecot Points 1749

En réponse aux commentaires que j'ai reçus, voici ce que j'ai fini par faire (si quelqu'un a des améliorations à apporter, n'hésitez pas à les poster) :

spec/concerns/personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end

2 votes

Oui, cela va casser les autres tests s'ils testent une vraie classe appelée Person . Je vais éditer pour corriger.

0 votes

Ça ne marche pas. Il me donne l'erreur : undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">

1 votes

Essayez d'inclure Personable plutôt que de l'étendre. Je vais mettre à jour la réponse.

8voto

lobati Points 621

Une autre idée est d'utiliser le avec_modèle gem pour tester ce genre de choses. Je cherchais à tester un problème moi-même et j'avais vu la pg_search gem fait ceci . C'est beaucoup mieux que de tester sur des modèles individuels, puisque ceux-ci peuvent changer, et c'est bien de définir les choses dont vous aurez besoin dans votre spécification.

2voto

Nathan Willson Points 37

Ce qui suit a fonctionné pour moi. Dans mon cas, mon souci était d'appeler les fichiers générés * _chemin et les autres approches n'ont pas semblé fonctionner. Cette approche vous permettra d'accéder à certaines des méthodes disponibles uniquement dans le contexte d'un contrôleur.

Préoccupation :

module MyConcern
  extend ActiveSupport::Concern

  def foo
    ...
  end
end

Spec :

require 'rails_helper'

class MyConcernFakeController < ApplicationController
  include MyConcernFakeController
end

RSpec.describe MyConcernFakeController, type: :controller do    
  context 'foo' do
    it '' do
      expect(subject.foo).to eq(...)
    end
  end
end

-3voto

Jin Lim Points 77

Il suffit d'inclure votre préoccupation dans la spécification et de la tester si elle renvoie la bonne valeur.

RSpec.describe Personable do
  include Personable

  context 'test' do
    let!(:person) { create(:person) }

    it 'should match' do
       expect(person.full_name).to eql 'David King'
    end
  end
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X