91 votes

rails rspec before all vs before each

contest_entry_spec.rb

    require 'spec_helper'

    describe ContestEntry do

      before(:all) do
        @admission=Factory(:project_admission)
        @project=Factory(:project_started, :project_type => @admission.project_type)
        @creative=Factory(:approved_creative, :creative_category => @admission.creative_category)
        @contest_entry=Factory(:contest_entry, :design_file_name => 'bla bla bla', :owner => @creative, :project => @project)
      end

      context 'non-specific tests' do
        subject { @contest_entry }
        it { should belong_to(:owner).class_name('User') }
        it { should belong_to(:project) }
        it { should have_many(:entry_comments) }

        it { should validate_presence_of(:owner) }
        it { should validate_presence_of(:project) }
        it { should validate_presence_of(:entry_no) }
        it { should validate_presence_of(:title) }

      end
end

Lorsque je lance ces tests, tout va bien, mais si je remplace before(:all) par before(:each), tous les tests échouent.

Voici l'erreur

 Failure/Error: @contest_entry=Factory(:contest_entry, :design_file_name => 'bla bla bla', :owner => @creative, :project => @project)
     ActiveRecord::RecordInvalid:
       Validation Failed: User is not allowed for this type of project

143voto

fontno Points 2412

before(:all) exécute le bloc une fois avant que tous les exemples ne soient exécutés.

before(:each) exécute le bloc une fois avant chacune de vos spécifications dans le fichier

before(:all) définit les variables d'instance @admission, @project, @creative, @contest_entry une seule fois avant que tous les it sont exécutés.

Cependant, :before(:each) réinitialise les variables d'instance dans le bloc avant chaque fois qu'un it est exécuté.

Il s'agit d'une distinction subtile mais importante

encore une fois,

before(:all)
#before block is run
it { should belong_to(:owner).class_name('User') }
it { should belong_to(:project) }
it { should have_many(:entry_comments) }

it { should validate_presence_of(:owner) }
it { should validate_presence_of(:project) }
it { should validate_presence_of(:entry_no) }
it { should validate_presence_of(:title) }

before(:each)
# before block
it { should belong_to(:owner).class_name('User') }
# before block
it { should belong_to(:project) }
# before block
it { should have_many(:entry_comments) }
# before block

# before block
it { should validate_presence_of(:owner) }
# before block
it { should validate_presence_of(:project) }
# before block
it { should validate_presence_of(:entry_no) }
# before block
it { should validate_presence_of(:title) }

53voto

wired00 Points 2951

Un détail important de la before :all est qu'il est pas DB transactional . C'est-à-dire tout ce qui se trouve à l'intérieur de la before :all persiste dans la base de données et vous devez la démanteler manuellement dans le fichier after :all méthode.

Cela signifie qu'une fois les suites de tests terminées, les modifications ne sont pas annulées et prêtes pour les tests ultérieurs. Cela peut entraîner des bogues complexes et des problèmes de contamination croisée des données. Par exemple, si une exception est levée, l'application after :all n'est pas appelé.

Cependant, before: each est Transaction DB.

Un test rapide pour le démontrer :

1. Tronquez votre table de base de données appropriée, puis essayez ceci,

  before :all do
    @user = Fabricate(:user, name: 'Yolo')
  end

2. Observer la base de données par la suite le modèle reste persistant !

after :all est nécessaire. Cependant, si une exception survient dans votre test, ce rappel ne se produira pas car le flux a été interrompu. La base de données sera laissée dans un état inconnu, ce qui peut être particulièrement problématique avec les environnements CI/CD et les tests automatisés.

3. Maintenant, essayez ceci,

  before :each do
    @user = Fabricate(:user, name: 'Yolo')
  end

4. Maintenant la base de données reste vierge de toute donnée une fois la suite de tests terminée. C'est beaucoup mieux et cela nous laisse un état cohérent après l'exécution des tests.

En bref, before :each est probablement ce que vous souhaitez. Vos tests seront légèrement plus lents, mais le jeu en vaut la chandelle.

Détail ici : https://relishapp.com/rspec/rspec-rails/docs/transactions Voir : Data created in before(:all) are not rolled back

J'espère que cela aidera un autre voyageur fatigué.

5voto

Jazib Bashir Points 106

before(:all) ce qui garantit que les utilisateurs de l'échantillon sont créés une seule fois, avant tous les tests du bloc. Il s'agit d'une optimisation de la vitesse.

2voto

shiva kumar Points 10174

Une chose à noter est que par défaut before utilise before(:each), aussi dans before(:all) l'instance du contrôleur n'est pas définie donc les méthodes du contrôleur comme request ne sont pas utilisées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X