我是模拟对象的新手,我正在尝试学习如何在RSpec中使用它们。有人可以发布一个示例(一个问候RSpec Mock对象世界类型示例),或者关于如何使用RSpec模拟对象API的链接(或任何其他参考)?
答案 0 :(得分:64)
以下是我在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
在这种情况下,我在嘲笑Page& PageType模型(对象)以及我调用的一些方法。
这使我能够运行这样的测试:
it "should be successful" do
Page.should_receive(:find_by_id).and_return(@page)
get 'show', :id => 1
response.should be_success
end
我知道这个答案更具针对性,但我希望它能帮到你一点点。
修改强>
好的,所以这是一个你好世界的例子......
给出以下脚本(hello.rb):
class Hello
def say
"hello world"
end
end
我们可以创建以下规范(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
答案 1 :(得分:6)
mock
已根据此github pull弃用。
现在我们可以使用double
- 更多here...
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
答案 2 :(得分:5)
我没有足够的观点对评论发表评论,但我想说接受的答案也帮助我试图找出如何以随机值存根。
我需要能够存储一个随机分配的对象实例值,例如:
class ClumsyPlayer < Player do
def initialize(name, health = 100)
super(name, health)
@health_boost = rand(1..10)
end
end
然后在我的规范中,我有一个问题,就是要弄清楚如何将笨拙的玩家的随机健康状态留下来测试当他们得到治疗时,他们会得到适当的健康促进。
诀窍是:
@player.stub!(health_boost: 5)
因此stub!
是关键所在,我一直在使用stub
,并且仍在获得随机的rspec传递和失败。
谢谢你Brian
答案 3 :(得分:1)
当前(3.x)RSpec提供纯模拟对象(如tokhi's answer中)和部分模拟(模拟对现有对象的调用)。这是部分嘲弄的一个例子。它使用expect
和receive
来模拟Order
对CreditCardService
的调用,以便只有在无需实际调用的情况下进行调用时,测试才会通过。
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
在这个例子中,mock是CreditCardService.instance
的返回值,可能是一个单例。
with
是可选的;没有它,任何对refund
的调用都会满足期望。可以使用and_return
给出返回值;在此示例中未使用它,因此调用返回nil
。
此示例使用RSpec的当前(expect .to receive
)模拟语法,该语法适用于任何对象。接受的答案使用旧的rspec-rails mock_model
方法,该方法特定于ActiveModel模型,并从rspec-rails移出到另一个gem。
答案 4 :(得分:0)
通常,当您想要将某些功能委托给其他对象但又不想在当前测试中测试实际功能时,您希望使用Mock对象,因此您可以将该对象替换为更易于控制的其他对象。让我们称这个对象为“依赖”......
您正在测试的东西(对象/方法/函数...)可以通过调用方法来与此依赖项进行交互...
当您使用依赖项“查询”某些内容时,您不需要使用“模拟API”,因为您可以只使用常规对象,并测试您正在测试的对象中的预期输出......例如:
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
当您想要改变您的依赖关系或做一些副作用时,例如在数据库中插入新记录,发送电子邮件,付款等等...现在而不是测试更改或副作用生成后,您只需检查您是否使用正确的属性调用正确的函数/方法...例如:
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
这是一个基本的例子,但你可以用这些知识做很多事情=)
我写了一篇关于这个内容的帖子,还有一些可能有用的帖子http://bhserna.com/2018/how-and-when-to-use-mock-objects-with-ruby-and-rspec.html