web-dev-qa-db-fra.com

Exemple d'objet simulé RSpec

Je suis nouveau dans la simulation d'objets et j'essaie d'apprendre à les utiliser dans RSpec. Quelqu'un peut-il s'il vous plaît publier un exemple (un bon exemple d'exemple de type monde RSpec Mock), ou un lien (ou toute autre référence) sur la façon d'utiliser l'API d'objet RSpec mock?

37
agentbanks217

Voici un exemple d'une maquette simple que j'ai faite pour un test de contrôleur dans une application Rails:

before(:each) do
  @page = mock_model(Page)
  @page.stub!(:path)
  @page.stub!(:find_by_id)
  @page_type = mock_model(PageType)
  @page_type.stub!(:name)
  @page.stub!(:page_type).and_return(@page_type)
end

Dans ce cas, je me moque des modèles Page & PageType (objets) ainsi que de certaines des méthodes que j'appelle.

Cela me donne la possibilité d'exécuter des tests comme celui-ci:

it "should be successful" do
  Page.should_receive(:find_by_id).and_return(@page)
  get 'show', :id => 1
  response.should be_success
end

Je sais que cette réponse est plus Rails spécifique, mais j'espère qu'elle vous aidera un peu.


Modifier

Ok, voici donc un exemple du monde bonjour ...

Étant donné le script suivant (hello.rb):

class Hello
  def say
    "hello world"
  end
end

Nous pouvons créer la spécification suivante (hello_spec.rb):

require 'rubygems'
require 'spec'

require File.dirname(__FILE__) + '/hello.rb'

describe Hello do
  context "saying hello" do 
    before(:each) do
      @hello = mock(Hello)
      @hello.stub!(:say).and_return("hello world")
    end

    it "#say should return hello world" do
      @hello.should_receive(:say).and_return("hello world")
      answer = @hello.say
      answer.should match("hello world")
    end
  end
end
64
Brian

mock est obsolète sur la base de ce github pull .

Maintenant, à la place, nous pouvons utiliser double - plus ici ...

 before(:each) do
   @page = double("Page")
   end

  it "page should return hello world" do
    allow(@page).to receive(:say).and_return("hello world")
    answer = @page.say
    expect(answer).to eq("hello world")
  end
6
tokhi

Je n'ai pas assez de points pour publier un commentaire sur une réponse, mais je voulais dire que la réponse acceptée m'a également aidé à essayer de comprendre comment insérer une valeur aléatoire.

J'avais besoin de pouvoir stub la valeur d'instance d'un objet qui est assignée au hasard, par exemple:

class ClumsyPlayer < Player do

  def initialize(name, health = 100)
    super(name, health)
    @health_boost = Rand(1..10)
  end
end

Ensuite, dans mes spécifications, j'ai eu un problème pour trouver comment bloquer la santé aléatoire du joueur maladroit pour tester que lorsqu'il obtient un soin, il obtient le bon coup de pouce pour sa santé.

L'astuce était:

@player.stub!(health_boost: 5)

Pour que stub! était la clé, je n'utilisais que stub et j'obtenais toujours des passes et des échecs rspec aléatoires.

Alors merci Brian

5
JoeManFoo

Normalement, vous souhaitez utiliser un objet simulé lorsque vous souhaitez déléguer certaines fonctionnalités à un autre objet, mais vous ne voulez pas tester la fonctionnalité réelle sur votre test actuel, vous remplacez donc cet objet par un autre plus facile à contrôler. Appelons cet objet "dépendance" ...

La chose que vous testez (objet/méthode/fonction ...) peut interagir avec cette dépendance en appelant des méthodes à ...

  • Recherchez quelque chose.
  • Changer quelque chose ou produire un effet secondaire.

Lors de l'appel d'une méthode pour rechercher quelque chose

Lorsque vous utilisez la dépendance pour "interroger" quelque chose, vous n'avez pas besoin d'utiliser "l'API fictive" car vous pouvez simplement utiliser un objet normal et tester la sortie attendue dans l'objet que vous testez ... par exemple:

describe "Books catalog" do
  class FakeDB
    def initialize(books:)
      @books = books
    end

    def fetch_books
      @books
    end
  end

  it "has the stored books" do
    db = FakeDB.new(books: ["Principito"])
    catalog = BooksCatalog.new(db)
    expect(catalog.books).to eq ["Principito"]
  end
end

Lors de l'appel d'une méthode pour changer quelque chose ou produire un effet secondaire ...

Lorsque vous souhaitez modifier votre dépendance ou faire quelque chose avec des effets secondaires comme insérer un nouvel enregistrement dans une base de données, envoyer un e-mail, effectuer un paiement, etc. maintenant au lieu de tester que le changement ou l'effet secondaire a été produit, vous vérifiez juste que vous appelez la bonne fonction/méthode avec les bons attributs ... par exemple:

describe "Books catalog" do
  class FakeDB
    def self.insert(book)
    end
  end

  def db
    FakeDB
  end

  it "stores new added books" do
    catalog = BooksCatalog.new(db)

    # This is how you can use the Mock API of rspec
    expect(db).to receive(:insert).with("Harry Potter")

    catalog.add_book("Harry Potter")
  end
end

Ceci est un exemple de base, mais vous pouvez faire beaucoup de choses juste avec cette connaissance =)

J'ai écrit un article avec ce contenu et un peu plus qui peut être utile http://bhserna.com/2018/how-and-when-to-use-mock -objects-with-Ruby-and-rspec.html

1
Benito Serna

La version actuelle (3.x) de RSpec fournit à la fois des objets simulés purs (comme dans réponse de tokhi ) et des moquages ​​partiels (appels moqueurs à un objet existant). Voici un exemple de moquerie partielle. Il utilise expect et receive pour simuler l'appel d'un Order à un CreditCardService, de sorte que le test ne passe que si l'appel est effectué sans avoir à effectuer réellement il.

class Order
  def cancel
     CreditCardService.instance.refund transaction_id
  end
end

describe Order do
  describe '#cancel' do
    it "refunds the money" do
      order = Order.new
      order.transaction_id = "transaction_id"
      expect(CreditCardService.instance).to receive(:refund).with("transaction_id")
      order.cancel
    end
  end
end

Dans cet exemple, la maquette est sur la valeur de retour de CreditCardService.instance, qui est vraisemblablement un singleton.

with est facultatif; sans lui, tout appel à refund satisferait l'attente. Une valeur de retour pourrait être donnée avec and_return; dans cet exemple, il n'est pas utilisé, donc l'appel renvoie nil.


Cet exemple utilise le courant de RSpec (expect .to receive) syntaxe moqueuse, qui fonctionne avec n'importe quel objet. La réponse acceptée utilise les anciens rspec-Rails mock_model, qui était spécifique aux modèles ActiveModel et a été déplacée de rspec-Rails vers une autre gemme.

1
Dave Schweisguth