web-dev-qa-db-fra.com

À quoi sert la méthode double dans rspec?

Il est indiqué dans la doc rspec que je devrais utiliser la méthode double afin de créer un test double. Mais je peux voir que cela fonctionne parfaitement bien même si je n'utilise pas double. Y a-t-il quelque chose de mal à ne pas utiliser double? Aussi, si je n'utilise pas deux fois comment MyClass obtient stub et d'autres méthodes rspec? Sont-ils disponibles pour tous les objets lors de l'exécution dans rspec?

require 'spec_helper'

class MyClass

    def self.run
        new.execute
    end

    def execute
        'foo'
    end

end

describe MyClass do

    it 'should stub instance method' do
        obj = MyClass.new
        obj.stub(:execute).and_return('bar')
        obj.execute.should == 'bar'
    end

    it 'should stub class method' do
        MyClass.stub(:run).and_return('baz')
        MyClass.run.should == 'baz'
    end

end
38
grafthez

Edit: je viens de relire votre question et j'ai réalisé que je n'y ai pas tout à fait répondu. Laissant ma réponse d'origine car elle est liée, mais voici votre réponse spécifique:

La raison pour laquelle vous n'avez pas besoin d'un double est parce que vous stubez les méthodes de classe, plutôt que les méthodes d'instance. double n'est utile que pour traiter les instances de la classe, pas la classe elle-même.

Ancienne réponse qui explique le double:

Vous devriez toujours utiliser de vraies classes au lieu de tests doubles lorsque vous le pouvez. Cela permettra d'exercer davantage votre code et de rendre vos tests plus complets. Les doubles de test sont utilisés dans des situations où vous ne pouvez pas ou ne devez pas utiliser un véritable objet. Par exemple, si une classe ne peut pas être instanciée sans toucher une ressource externe (comme un réseau ou une base de données), ou a un grand nombre de dépendances, et que vous testez simplement quelque chose qui l'utilise, vous voudrez peut-être créer un double et stub quelques méthodes sur le double.

Voici un exemple plus spécifique: disons que vous testez MyClass, mais pour instancier MyClass, vous devez passer un FooLogger:

mylogger = FooLogger.new
myclass = MyClass.new logger: mylogger

Si FooLogger.new ouvre un socket syslog et commence à le spammer immédiatement, chaque fois que vous exécutez vos tests, vous vous connecterez. Si vous ne souhaitez pas spammer vos journaux pendant ce test, vous pouvez créer un double pour FooLogger et y ajouter une méthode:

mylogger = double(FooLogger)
mylogger.stub(:log)
myclass = MyClass.new logger: mylogger

Étant donné que la plupart des classes bien conçues peuvent être instanciées sans aucun effet secondaire, vous pouvez généralement simplement utiliser l'objet réel au lieu d'un double et les méthodes de stub à la place. Il existe d'autres scénarios où les classes ont de nombreuses dépendances qui les rendent difficiles à instancier, et les doubles sont un moyen de surmonter la cruauté et de tester la chose qui vous tient vraiment à cœur.

D'après mon expérience, avoir besoin d'utiliser un double est une odeur de code, mais nous devons souvent utiliser des classes que nous ne pouvons pas facilement changer (par exemple à partir d'un joyau), c'est donc un outil dont vous pourriez avoir besoin de temps en temps.

43
Jim Stewart

Avec RSpec Mocks 3.0, le comportement des doubles a changé. Vous pouvez maintenant vérifier les doubles , ce qui signifie que "RSpec vérifiera que les méthodes qui sont tronquées sont réellement présentes sur l'objet sous-jacent s'il est disponible", mais "aucune vérification ne se produira si l'objet ou la classe sous-jacent est non défini".

La vérification des doubles vous demande d'être précis sur le type double (instance, classe, objet, classe dynamique, partiel). Voici un exemple tiré de RSpec Relish pour un double d'instance:

RSpec.describe User, '#suspend!' do
  it 'notifies the console' do
    notifier = instance_double("ConsoleNotifier")

    expect(notifier).to receive(:notify).with("suspended as")

    user = User.new(notifier)
    user.suspend!
  end
end
0
georgschlenkhoff