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?
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
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
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
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 à ...
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
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
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.