web-dev-qa-db-fra.com

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

J'ai commencé mon voyage avec TDD en Rails et j'ai rencontré un petit problème concernant les tests de validation de modèle auquel je n'arrive pas à trouver de solution. Disons que j'ai un modèle utilisateur,

class User < ActiveRecord::Base
  validates :username, :presence => 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 présence, mais que faire si je veux être plus précis? Par exemple, tester full_messages sur l'objet erreurs.

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

Ma préoccupation concernant la tentative initiale (en utilisant should_not be_valid) 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 présente un inconvénient mineur: il utilise la méthode create au lieu de la nouvelle méthode afin d'obtenir l'objet erreurs.

Je voudrais que mes tests soient plus précis sur ce qu'ils testent, mais en même temps ne pas avoir à toucher à une base de données.

Quelqu'un a-t-il une entrée?

69
Feech

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

Deuxièmement, FÉLICITATIONS pour votre tentative de TDD avec ROR Je vous promets qu'une fois que vous aurez commencé, vous ne regarderez pas en arrière.

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

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

MAIS ce que je suggère, c'est de mettre en place des usines pour tous vos modèles qui généreront automatiquement un modèle valide pour vous et ensuite vous pourrez brouiller les attributs individuels et voir si votre validation. J'aime utiliser FactoryGirl pour cela:

Fondamentalement, une fois que vous avez configuré votre test, cela 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 ouais et voici ne bonne diffusion sur rail qui explique tout mieux que moi:

bonne chance :)


MISE À JOUR: Depuis version 3. la syntaxe pour Factory Girl a changé. J'ai modifié mon exemple de code pour refléter cela.

96
Matthew

Un moyen plus facile de tester les validations de modèles (et beaucoup plus d'enregistrements actifs) consiste à utiliser un joyau comme shoulda ou remarquable .

Ils permettront au test comme suit:

describe User

  it { should validate_presence_of :name }

end
43
nathanvda

Essaye ça:

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

dans la nouvelle version rspec, vous devez utiliser expect à la place de should, sinon vous obtiendrez 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
4
dayudodo

J'ai traditionnellement traité les spécifications de contenu d'erreur dans les spécifications de fonctionnalité ou de demande. Ainsi, par exemple, j'ai une spécification similaire que je vais condenser ci-dessous:

Exemple de spécification de fonctionnalité

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")

Alors, j'ai mes spécifications de modèle testant si quelque chose est valide, mais ensuite mes spécifications de fonctionnalité qui testent la sortie exacte du message d'erreur. Pour info, ces spécifications de fonctionnalités nécessitent Capybara qui peut être trouvé ici .

0
aceofbassgreg

Comme @nathanvda l'a dit, je profiterais de la gemme Shoulda Matchers de Thoughtbot. Avec ce basculement, 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
0
LaCroixed

Un peu tard pour la fête ici, mais si vous ne voulez pas ajouter d'égaliseurs shoulda, 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
0
marksiemers