Rails 3.1, RSpec: test des validations du modèle

J’ai commencé mon parcours avec TDD dans Rails et j’ai rencontré un petit problème concernant les tests de validation des modèles auxquels je n’arrive pas à trouver de solution. Disons que j’ai un modèle d’utilisateur,

class User  true end 

et un test simple

 it "should require a username" do User.new(:username => "").should_not be_valid end 

Cela teste correctement la validation de la présence, mais que faire si je veux être plus précis? Par exemple, tester full_messages sur l’object error.

 it "should require a username" do user = User.create(:username => "") user.errors[:username].should ~= /can't be blank/ end 

Ce qui m’inquiète lors de la première tentative (avec should_not be_valid), c’est que RSpec ne produira pas de message d’erreur descriptif. Il dit simplement “attendu valide? Pour retourner faux, c’est vrai.” Cependant, le deuxième exemple de test a un inconvénient mineur: il utilise la méthode create au lieu de la nouvelle méthode pour obtenir l’object error.

J’aimerais que mes tests soient plus précis sur ce qu’ils testent, mais en même temps, ils ne doivent pas toucher à une firebase database.

Quelqu’un at-il des commentaires?

Je voudrais d’abord dire que vous avez un nom méchant.

Deuxièmement, les félicitations à l’égard de vos efforts en matière de TDD avec ROR, je vous le promets, une fois que vous aurez commencé, vous ne pourrez plus regarder en arrière.

La solution la plus simple et la plus rapide sera de générer un nouveau modèle valide avant chacun de vos tests, comme ceci:

  before(:each) do @user = User.new @user.username = "a valid username" end 

MAIS, ce que je suggère, c’est que vous définissiez des usines pour tous vos modèles qui génèreraient automatiquement un modèle valide pour vous, puis vous pourrez vous débrouiller avec des atsortingbuts individuels et voir si votre validation. J’aime utiliser FactoryGirl pour cela:

Fondamentalement, une fois que vous avez mis en place votre test ressemblerait à ceci:

 it "should have valid factory" do FactoryGirl.build(:user).should be_valid end it "should require a username" do FactoryGirl.build(:user, :username => "").should_not be_valid end 

Oh et voilà un bon railcast qui explique tout mieux que moi:

bonne chance 🙂


MISE À JOUR: À partir de la version 3.0, la syntaxe de Factory Girl a été modifiée. J’ai modifié mon exemple de code pour refléter cela.

Un moyen plus facile de tester les validations de modèles (et bien plus encore les enregistrements actifs) consiste à utiliser une gemme comme musta ou remarquable .

Ils permettront le test comme suit:

 describe User it { should validate_presence_of :name } end 

Essaye ça:

 it "should require a username" do user = User.create(:username => "") user.valid? user.errors.should have_key(:username) end 

dans la nouvelle version de rspec, vous devriez plutôt utiliser expect, sinon vous aurez un avertissement:

 it "should have valid factory" do expect(FactoryGirl.build(:user)).to be_valid end it "should require a username" do expect(FactoryGirl.build(:user, :username => "")).not_to be_valid end 

J’ai toujours traité les spécifications de contenu d’erreur dans les spécifications de fonctionnalités ou de requêtes. Donc, par exemple, j’ai une spécification similaire que je vais condenser ci-dessous:

Feature Spec Exemple

 before(:each) { visit_order_path } scenario 'with invalid (empty) description' , :js => :true do add_empty_task #this line is defined in my spec_helper expect(page).to have_content("can't be blank") 

J’ai donc mes spécifications de modèle pour tester si quelque chose est valide, mais ensuite mes spécifications de fonctionnalité qui testent la sortie exacte du message d’erreur. FYI, ces spécifications de fonctionnalités nécessitent Capybara qui peut être trouvé ici .

Comme @nathanvda a dit, je profiterais de la gemme Shoulda Matchers de Thoughtbot . Avec cette bascule, vous pouvez écrire votre test de la manière suivante pour tester la présence, ainsi que tout message d’erreur personnalisé.

 RSpec.describe User do describe 'User validations' do let(:message) { "I pitty da foo who dont enter a name" } it 'validates presence and message' do is_expected.to validate_presence_of(:name). with_message message end # shorthand syntax: it { is_expected.to validate_presence_of(:name).with_message message } end end 

Un peu tard pour la fête ici, mais si vous ne voulez pas append de match, cela devrait fonctionner avec rspec-rails et factorybot:

 # ./spec/factories/user.rb FactoryBot.define do factory :user do sequence(:username) { |n| "user_#{n}" } end end # ./spec/models/user_spec.rb describe User, type: :model do context 'without a username' do let(:user) { create :user, username: nil } it "should NOT be valid with a username error" do expect(user).not_to be_valid expect(user.errors).to have_key(:username) end end end