Comment tester un problème dans les rails

104

Étant donné que j'ai un Personableproblème dans mon application Rails 4 qui a une full_nameméthode, comment procéder pour tester cela à l'aide de RSpec?

préoccupations / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
Kyle Decot
la source
Quel cadre de test utilisez-vous? Rappelez-vous également que Personable n'est qu'un module Ruby normal. Testez-le comme vous testeriez n'importe quel autre mixin.
Lee Jarvis
N'a pas ActiveSupport::Concernété retiré de Rails? Je pensais que ça s'était passé il y a un moment.
Russell
@LeeJarvis J'utilise Rspec avec FactoryGirl
Kyle Decot
4
@Russell Je suis d'accord. Cela dit, je n'aiderais pas quelqu'un avec ses questions simplement parce qu'il suivait une façon Rails-y de faire quelque chose avec lequel je n'étais pas d'accord. Quoi qu'il en soit, c'est un peu échapper au sujet de cette question :-)
Lee Jarvis

Réponses:

176

La méthode que vous avez trouvée fonctionnera certainement pour tester un peu de fonctionnalité mais semble assez fragile - votre classe fictive (en fait, juste un Struct dans votre solution) peut ou non se comporter comme une vraie classe qui includevous concerne. De plus, si vous essayez de tester les problèmes de modèle, vous ne pourrez pas faire des choses comme tester la validité des objets ou appeler des rappels ActiveRecord à moins que vous ne configuriez la base de données en conséquence (car votre classe factice n'aura pas de support de table de base de données il). De plus, vous voudrez non seulement tester le problème, mais également tester le comportement du problème dans les spécifications de votre modèle.

Alors pourquoi ne pas faire d'une pierre deux coups? En utilisant les groupes d'exemples partagés de RSpec , vous pouvez tester vos préoccupations par rapport aux classes réelles qui les utilisent (par exemple, les modèles) et vous pourrez les tester partout où elles sont utilisées. Et vous ne devez écrire les tests qu'une seule fois, puis les inclure dans n'importe quelle spécification de modèle qui utilise votre préoccupation. Dans votre cas, cela pourrait ressembler à 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 rappels AR, où rien de moins qu'un objet AR ne fera tout simplement pas l'affaire.

Josh Leitzel
la source
2
Un inconvénient est que cela ralentira parallel_tests. Je pense qu'il vaudra mieux avoir des tests séparés au lieu d'utiliser shared_examples_foret it_behaves_like.
Artem Kalinchuk
6
@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 des tests.
Aaron K
8
Assurez-vous d'inclure le concernsrépertoire dans votre spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy
1
Je n'ai rien trouvé sur l'inclusion du répertoire des préoccupations dans ce lien. Pourriez-vous clarifier comment cela se fait? Je ne parviens pas à faire reconnaître le module par mon test RSpec dans l'une de mes préoccupations.
Jake Smith
4
_specN'ajoutez pas au nom de fichier qui contient shared_examples_for (personable_spec.rb dans ce cas), sinon vous obtiendrez un message d'avertissement trompeur - github.com/rspec/rspec-core/issues/828 .
Lalu
63

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, n'hésitez pas à les publier) :

spec / concern / 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
Kyle Decot
la source
1
Oui, cela cassera d'autres tests s'ils testent une vraie classe appelée Person. Je vais modifier pour corriger.
Russell
Cela ne marche pas. Ça me donne l'erreur:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot
Essayez d'inclure Personable plutôt que de l'étendre. Je mettrai à jour la réponse.
Russell
Fonctionne très bien maintenant. Merci de m'avoir pointé dans la bonne direction et de m'aider à refactoriser @Russell
Kyle Decot
Fonctionne très bien et a l'air bien
Edward
7

Une autre idée est d'utiliser la gemme with_model pour tester des choses comme celle-ci. Je cherchais moi-même à tester un problème et j'avais vu le gem pg_search faire cela . Cela semble beaucoup mieux que de tester des modèles individuels, car ceux-ci peuvent changer, et il est bon de définir les éléments dont vous aurez besoin dans vos spécifications.

lobati
la source