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?
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.
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
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 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
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 .
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
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