[RUBY] Problème "L'e-mail a déjà été pris" et solution de contournement dans le test d'unicité d'e-mail de RSpec

problème

Lors de l'écriture d'un test pour le modèle User dans Rspec

Dans un test pour confirmer l'unicité de l'email Si vous utilisez FactoryBot.create (), le même test sera répété plusieurs fois par la suite. L'erreur suivante s'affiche dans le deuxième test et les suivants

ActiveRecord::RecordInvalid:
  Validation failed: Email has already been taken

Cause

Il semble que la base de données ne soit pas annulée pour chaque test.

Les paramètres suivants ont été définis dans spec / rails_helper.rb (par défaut)

spec/rails_helper.rb


config.use_transactional_fixtures = true

Solution

1) Initialiser DB pour chaque test

rails db:migrate:reset RAILS_ENV=test

Après cela, le test n'a réussi qu'une seule fois

2) Réécrire le code de test

spec/factories/users.rb

FactoryBot.define do
  factory :user, class: User do
    username {"test_user"}
    email {"[email protected]"}
    password {"password"}
    password_confirmation {"password"}
  end
  factory :existed_user, class: User do
    username { "existed_user" }
    email { "[email protected]" }
    password { "password" }
    password_confirmation { "password" }
  end
end

spec/models/user_spec.rb

subject(:user) { FactoryBot.build(:user, email: email) }

context "Lorsque les e-mails sont dupliqués" do
  existed_user =  User.find_by(username: "existed_user") || FactoryBot.create(:existed_user)
  let(:email) { existed_user.email }
  it { is_expected.to be_invalid }
end

User.find_by(username: "existed_user") || FactoryBot.create(:existed_user)Seulement lorsque cela est nécessaire dans la partie decreateRéécrit pour faire

Il semble y avoir une solution plus essentielle utilisant des gemmes ou autre chose, Jusqu'à présent, le problème peut être évité par la méthode de 2).

référence

En outre, la définition séquentielle de l'usine a été introduite dans plusieurs articles. Seulement 5 choses à retenir lors de l'utilisation de FactoryBot-Qiita

Conseils: définition de classe pour l'usine

Aussi, FactoryBot.create (: exist_user) et Lorsque vous faites FactoryBot.build (: exist_user) Vous devez spécifier class: User dans FactoryBot.define A rencontré l'erreur suivante

NameError: uninitialized constant ExistedUser

Puisque FactoryBot.create (: user) et FactoryBot.build (: user) sont bien Il semble qu'il s'agit de la classe User sans autorisation

Premier RSpec Résultat J'ai écrit un test du modèle utilisateur comme suit

require 'rails_helper'

describe User do
  describe "#create" do
    it "username, email, password,Vous pouvez vous inscrire si la confirmation du mot de passe existe" do
      user = FactoryBot.build(:user)
      expect(user).to be_valid
    end
    describe "username" do
      subject(:user) { FactoryBot.build(:user, username: username) }
      context "Impossible de s'inscrire si le nom d'utilisateur n'existe pas" do
        let(:username) { "" }
        it { is_expected.to be_invalid }
      end
      context "Peut être enregistré lorsque le nom d'utilisateur comporte 12 caractères" do
        let(:username) { "a"*12 }
        it { is_expected.to be_valid }
      end
      context "Ne peut pas être enregistré si le nom d'utilisateur comporte 13 caractères" do
        let(:username) { "a"*13 }
        it { is_expected.to be_invalid }
      end
      context "Si le nom d'utilisateur est dupliqué" do
        existed_user =  User.find_by(username: "existed_user") || FactoryBot.create(:existed_user)
        let(:username) { existed_user.username }
      end
    end
    describe "email" do
      subject(:user) { FactoryBot.build(:user, email: email) }
      context "Si l'e-mail n'existe pas" do
        let(:email) { "" }
        it { is_expected.to be_invalid }
      end
      context "Lorsque les e-mails sont dupliqués" do
        existed_user =  User.find_by(username: "existed_user") || FactoryBot.create(:existed_user)
        let(:email) { existed_user.email }
        it { is_expected.to be_invalid }
      end
    end
    describe "password" do
      subject(:user) { FactoryBot.build(:user, password: password, password_confirmation: password_confirmation) }
      context "Si le mot de passe n'existe pas" do
        let(:password) { "" }
        let(:password_confirmation) { "password" }
        it { is_expected.to be_invalid }
      end
      context "Si le mot de passe existe mais que la confirmation du mot de passe n'existe pas" do
        let(:password) { "passowrd" }
        let(:password_confirmation) { "" }
        it { is_expected.to be_invalid }
      end
      context "le mot de passe est un mot de passe_S'il ne correspond pas à la confirmation" do
        let(:password) { "password_a" }
        let(:password_confirmation) { "password_b" }
        it { is_expected.to be_invalid }
      end
      context "Si le mot de passe comporte 7 caractères" do
        let(:password) { "a"*7 }
        let(:password_confirmation) { "a"*7 }
        it { is_expected.to be_invalid }
      end
      context "Si le mot de passe comporte 8 caractères" do
        let(:password) { "a"*8 }
        let(:password_confirmation) { "a"*8 }
        it { is_expected.to be_valid }
      end
    end
  end
end

Recommended Posts

Problème "L'e-mail a déjà été pris" et solution de contournement dans le test d'unicité d'e-mail de RSpec
ActiveRecord :: RecordInvalid: la validation a échoué: l'e-mail a déjà été pris en erreur dans le test RSpec