web-dev-qa-db-fra.com

Comment dois-je stub une méthode globalement en utilisant RSpec?

Je travaille sur une application Rails. J'essaie de stub une méthode globalement.

Ce que je fais, c'est de l'intégrer dans la configuration RSpec, sur un bloc before(:suite) comme suit:

RSpec.configure do |config|
  config.before(:suite) do
    allow_any_instance_of(MyModel).to receive(:my_method).and_return(false)
  end
end

Cependant, le démarrage du test échoue avec l'erreur suivante:

in `method_missing': undefined method `allow_any_instance_of' for #<RSpec::Core::ExampleGroup:0x00000008d6be08> (NoMethodError)

Un indice? Comment dois-je stub une méthode globalement en utilisant RSpec?

P.

27
p.matsinopoulos

Il s'agit probablement d'un problème de contexte/d'initialisation. Le faire dans config.before(:each) devrait résoudre votre problème.

19
Ioannis Karadimas

Ne stubez pas les méthodes dans before(:suite) car les stubs sont effacés après chaque exemple, comme indiqué dans le rspec-mocks README :

Utilisez before(:each), pas before(:all)

Les stubs dans before(:all) ne sont pas pris en charge. La raison en est que tous les stubs et les mocks sont effacés après chaque exemple, donc tout stub défini dans before(:all) fonctionnerait dans le premier exemple qui se produit dans ce groupe, mais pas pour les autres.

Au lieu de before(:all), utilisez before(:each).

Je pense que c'est pourquoi allow_any_instance_of N'est pas disponible dans le bloc before(:suite), mais est disponible dans le bloc before(:each).

Si la méthode est toujours manquante, vous avez peut-être configuré rspec-mocks pour n'autoriser que la syntaxe :should. allow_any_instance_of A été introduit dans RSpec 2.14 avec toute la nouvelle syntaxe :expect Pour les attentes de message.

Assurez-vous que cette syntaxe est activée en inspectant la valeur de RSpec::Mocks.configuration.syntax. Il s'agit d'un tableau des syntaxes disponibles dans rspec-mocks. Les syntaxes disponibles sont :expect Et :should.

RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.syntax = [:expect, :should]
  end
end

Une fois configuré correctement, vous devriez pouvoir utiliser allow_any_instance_of.

15
cbliard

J'ai récemment rencontré un cas où j'avais besoin de coller quelque chose dans un bloc before(:all) ou before(:context), et j'ai trouvé les solutions ici pour ne pas fonctionner pour mon cas d'utilisation.

Documents RSpec sur avant () et après () hooks dit que ce n'est pas supporté:

avant et après les hooks peuvent être définis directement dans les groupes d'exemples dans lesquels ils doivent s'exécuter, ou dans un bloc global RSpec.configure.

AVERTISSEMENT: la définition de variables d'instance n'est pas prise en charge dans (: suite).

AVERTISSEMENT: les maquettes ne sont prises en charge qu'avant (: exemple).

Remarque: les étendues: example et: context sont également disponibles en tant que: each et: all, respectivement. Utilisez celui que vous préférez.

Problème

Je faisais n joyau pour écrire un format de fichier binaire qui contenait à l'horodatage Epix unix dans son en-tête binaire. Je voulais écrire des tests RSpec pour vérifier l'exactitude de l'en-tête du fichier de sortie et le comparer à un fichier de référence binaire de test. Afin de créer des tests rapides, j'avais besoin d'écrire le fichier une fois avant que tous les exemples de blocs de groupe ne s'exécutent. Afin de vérifier l'horodatage par rapport au fichier de référence, j'ai dû forcer Time.now() pour renvoyer une valeur constante. Cela m'a conduit sur la voie d'essayer de stub Time.now Pour retourner ma valeur cible.

Cependant, comme rspec/mocks Ne supportait pas le stubbing dans un bloc before(:all) ou before(:context), cela ne fonctionnait pas. L'écriture du fichier before(:each) a causé d'autres problèmes étranges.

Heureusement, je suis tombé sur problème # 240 de rspec-mocks qui avait la solution!

Solution

Depuis le 9 janvier 2014 ( rspec-mocks PR # 519 ) RSpec contient maintenant une méthode pour contourner ce problème:

RSpec::Mocks.with_temporary_scope

Exemple

require 'spec_helper'
require 'rspec/mocks'

describe 'LZOP::File' do
  before(:all) {
    @expected_lzop_magic = [ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a ]
    @uncompressed_file_data = "Hello World\n" * 100
    @filename = 'lzoptest.lzo'
    @test_fixture_path = File.join(File.dirname(__FILE__), '..', 'fixtures', @filename + '.3')
    @lzop_test_fixture_file_data = File.open( @test_fixture_path, 'rb').read
    @tmp_filename = File.basename(@filename)
    @tmp_file_path = File.join( '', 'tmp', @tmp_filename)

    # Stub calls to Time.now() with our fake mtime value so the mtime_low test against our test fixture works
    # This is the mtime for when the original uncompressed test fixture file was created
    @time_now = Time.at(0x544abd86)
  }

  context 'when given a filename, no options and writing uncompressed test data' do

    describe 'the output binary file' do
      before(:all) {
        RSpec::Mocks.with_temporary_scope do
          allow(Time).to receive(:now).and_return(@time_now)
          # puts "TIME IS: #{Time.now}"
          # puts "TIME IS: #{Time.now.to_i}"
          my_test_file = LZOP::File.new( @tmp_file_path )
          my_test_file.write( @uncompressed_file_data )
          @test_file_data = File.open( @tmp_file_path, 'rb').read
        end
      }

      it 'has the correct magic bits' do
        expect( @test_file_data[0..8].unpack('C*') ).to eq @expected_lzop_magic
      end

      ## [...SNIP...] (Other example blocks here)
      it 'has the original file mtime in LZO file header' do
        # puts "time_now= #{@time_now}"

        if @test_file_data[17..21].unpack('L>').first & LZOP::F_H_FILTER == 0
          mtime_low_start_byte=25
          mtime_low_end_byte=28
          mtime_high_start_byte=29
          mtime_high_end_byte=32
        else
          mtime_low_start_byte=29
          mtime_low_end_byte=32
          mtime_high_start_byte=33
          mtime_high_end_byte=36
        end
        # puts "start_byte: #{start_byte}"
        # puts "end_byte: #{end_byte}"
        # puts "mtime_low: #{@test_file_data[start_byte..end_byte].unpack('L>').first.to_s(16)}"
        # puts "test mtime: #{@lzop_test_fixture_file_data[start_byte..end_byte].unpack('L>').first.to_s(16)}"

        mtime_low = @test_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack('L>').first
        mtime_high = @test_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack('L>').first
        # The testing timestamp has no high bits, so this test should pass:
        expect(mtime_low).to eq @time_now.to_i
        expect(mtime_high).to eq 0

        expect(mtime_low).to eq @lzop_test_fixture_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack('L>').first
        expect(mtime_high).to eq @lzop_test_fixture_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack('L>').first

        mtime_fixed = ( mtime_high << 16 << 16 ) | mtime_low

        # puts "mtime_fixed: #{mtime_fixed}"
        # puts "mtime_fixed: #{mtime_fixed.to_s(16)}"

        expect(mtime_fixed).to eq @time_now.to_i

      end
    end
  end
end
8
TrinitronX

Si vous voulez qu'une méthode particulière se comporte d'une certaine manière pour l'ensemble de votre suite de tests, il n'y a même aucune raison de traiter les stubs de RSpec. Au lieu de cela, vous pouvez simplement (re) définir la méthode pour vous comporter comme vous le souhaitez dans votre environnement de test:

class MyModel
  def my_method
    false
  end
end

Cela pourrait aller dans spec/spec_helper.rb ou un fichier similaire.

5
Myron Marston

Quelle version de RSpec utilisez-vous? Je crois allow_any_instance_of a été introduit dans RSpec 2.14. Pour les versions antérieures, vous pouvez utiliser:

MyModel.any_instance.stub(:my_method).and_return(false)
2
Erik Ostrom

Vous pouvez utiliser ce qui suit pour écraser une méthode 'do_this' de la classe 'Xyz':

allow_any_instance_of(Xyz).to receive(:do_this).and_return(:this_is_your_stubbed_output)

Cela stubsque la sortie vers - ': this_is_your_stubbed_output' d'où que cette fonction soit invoquée.

Vous pouvez utiliser le morceau de code ci-dessus dans le bloc avant (: chaque) pour le rendre applicable à tous vos exemples de spécifications.

1
Pushp Raj Saurabh