使用Minitest模仿OpenURI.open_uri [英] Idiomatically mock OpenURI.open_uri with Minitest
问题描述
我有调用OpenURI.open_uri
的代码,我想确认调用中使用的URI(因此存根对我来说不起作用),但也拦截了该调用.我希望不必出于测试目的而抽象掉对OpenURI.open_uri
的调用.我提出的内容似乎冗长且过于复杂.
under_test.rb
require 'open-uri'
class UnderTest
def get_request(uri)
open(uri).read
end
end
test_under_test.rb
require 'minitest/autorun'
require './lib/under_test'
class TestUnderTest < Mintest::Test
def test_get_request
@under_test = UnderTest.new
mock_json = '{"json":[{"element":"value"}]}'
uri = URI('https://www.example.com/api/v1.0?attr=value&format=json')
tempfile = Tempfile.new('tempfile')
tempfile.write(mock_json)
mock_open_uri = Minitest::Mock.new
mock_open_uri.expect(:call, tempfile, [uri])
OpenURI.stub :open_uri, mock_open_uri do
@under_test.get_request('https://www.example.com/api/v1.0?attr=value&format=json'
end
mock_open_uri.verify
end
end
我是滥用还是误解了Minitest的嘲笑?
跳舞的一部分是,我还创建了Tempfile
,这样我的read
调用成功.我可以解决这个问题,但我希望有一种方法可以使呼叫链更接近起点.
对于此问题,可以使用测试间谍:
测试间谍是一个函数,它记录参数,返回值,此值以及所有调用所引发的异常(如果有). 测试间谍可以是匿名函数,也可以包装现有函数.
对于Minitest
,我们可以使用gem 间谍.
在安装和 (1):由于Ruby的 鸭子键入 ,您实际上不需要在测试中提供将在应用程序的非测试运行中创建的确切对象. 让我们看看您的 实际上,在生产"环境中的 在这里,我使用了 OpenStruct 的功能,以构建某物,该内容将响应 在我们的测试案例中,我们提供了 minimum (最小)数量的代码,以使测试通过.我们不在乎其他 (2):我们在 它抛出异常,即 轮到将 此外,我们使用以下代码定义方法调用将返回的内容: 执行测试脚本时,将执行 测试可能出问题的地方 好吧,到目前为止,我们已经看到脚本可以继续进行而没有任何问题,但是我想强调一个示例,说明在 让我们修改一下测试: 运行时会失败,并显示类似以下内容: 我们用 http://google.com 来调用 这证明我们的 答案很长,但是我试图提供尽可能最好的解释,但是我不希望所有事情都一目了然-如果您有任何疑问,我很乐意为您提供帮助,并相应地更新答案! 祝你好运! I have code that invokes under_test.rb test_under_test.rb Am I misusing or misunderstanding Minitest's mocking? Part of the dancing around is that I'm also creating a For this problem, test-spies could be the way to go: A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function. taken from: http://sinonjs.org/docs/ For After installing, and including it in our test environment, the test can be rearranged as follows: (1): Because of duck typing nature of Ruby, you don't really need to provide in your tests exact objects that would be created in non-test run of your application. Let's take a look at your In fact, Here I used the power of OpenStruct, to build something, that will respond to In our test case we're providing the minimal amount of code, to make the test pass. We don't care about other (2): We're creating a spy on it throws exception, that the It turns that the Additionally, we define what will be returned by method call with following code: When our test script executes, the Test what can go wrong Ok, for now we've seen that our script proceeded without any problems, but I'd like to highlight the example of how assertion on our Let's modify a bit the test: Which when run, will fail with something similar to: We have called our This proves our It's quite long answer, but I tried to provide the best possible explanation, however I don't expect all things are clear - if you have any questions, I'm more than happy to help, and update the answer accordingly! Good luck! 这篇关于使用Minitest模仿OpenURI.open_uri的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!UnderTest
类:class UnderTest
def get_request(uri)
open(uri).read
end
end
open
可能返回Tempfile
的实例,该实例使用方法read
会出错.但是,在您的测试"环境中,存根" 时,您无需提供类型为Tempfile
的真实"对象.足以提供任何,使令人毛骨悚然一样.read
消息.让我们仔细看看:require 'ostruct'
tempfile = OpenStruct.new(read: "Example output")
tempfile.read # => "Example output"
Tempfile
方法,因为我们的测试仅依赖read
.open
方法中创建 spy Kernel
模块,这可能会造成混淆,因为我们需要OpenURI
模块.当我们尝试时:Spy.on_instance_method(OpenURI, :open)
NameError: undefined method `open' for module `OpenURI'
open
方法附加到提到的Kernel
模块.and_return { OpenStruct.new(read: mock_json) }
@test_under.get_request(test_uri)
,该注册将open
方法调用及其自变量注册到我们的spy
对象上.我们可以通过(3)来断言.
spy
上的断言可能会失败的情况. class TestUnderTest < Minitest::Test
def test_get_request
open_spy = Spy.on_instance_method(Kernel, :open)
.and_return { OpenStruct.new(read: "whatever") }
UnderTest.new.get_request("http://google.com")
assert open_spy.has_been_called_with?("http://yahoo.com")
end
end
1) Failure:
TestUnderTest#test_get_request [test/lib/test_under_test.rb:17]:
Failed assertion, no message given.
get_request
,但断言spy
可以正常工作.OpenURI.open_uri
and I want to confirm the URI being used in the call (so a stub isn't going to work for me), but also intercept the call. I'm hoping not to have to abstract away the call to OpenURI.open_uri
just for test purposes. What I've come up with seems verbose and overly complicated.require 'open-uri'
class UnderTest
def get_request(uri)
open(uri).read
end
end
require 'minitest/autorun'
require './lib/under_test'
class TestUnderTest < Mintest::Test
def test_get_request
@under_test = UnderTest.new
mock_json = '{"json":[{"element":"value"}]}'
uri = URI('https://www.example.com/api/v1.0?attr=value&format=json')
tempfile = Tempfile.new('tempfile')
tempfile.write(mock_json)
mock_open_uri = Minitest::Mock.new
mock_open_uri.expect(:call, tempfile, [uri])
OpenURI.stub :open_uri, mock_open_uri do
@under_test.get_request('https://www.example.com/api/v1.0?attr=value&format=json'
end
mock_open_uri.verify
end
end
Tempfile
so that my read
call succeeds. I could stub that out but I'm hoping there's a way I could head off the call chain closer to the beginning.
Minitest
we can use gem spy.require 'minitest/autorun'
require 'spy/integration'
require 'ostruct' # (1)
require './lib/under_test'
class TestUnderTest < Minitest::Test
def test_get_request
mock_json = '{"json":[{"element":"value"}]}'
test_uri = URI('https://www.example.com/api/v1.0?attr=value&format=json')
open_spy = Spy.on_instance_method(Kernel, :open) # (2)
.and_return { OpenStruct.new(read: mock_json) } # (1)
@under_test = UnderTest.new
assert_equal @test_under.get_request(test_uri), mock_json
assert open_spy.has_been_called_with?(test_uri) # (3)
end
end
UnderTest
class:class UnderTest
def get_request(uri)
open(uri).read
end
end
open
in "production" environment could return instance of Tempfile
, which quacks with method read
. However in your "test" environment, when "stubbing", you don't need to provide "real" object of type Tempfile
. It is enough, to provide anything, that quacks like one.read
message. Let's take a look at it closer:require 'ostruct'
tempfile = OpenStruct.new(read: "Example output")
tempfile.read # => "Example output"
Tempfile
methods, because our tests rely only on read
.open
method in Kernel
module, which might be confusing, because we're requiring OpenURI
module. When we try:Spy.on_instance_method(OpenURI, :open)
NameError: undefined method `open' for module `OpenURI'
open
method is attached to mentioned Kernel
module.and_return { OpenStruct.new(read: mock_json) }
@test_under.get_request(test_uri)
is performed, which registers the open
method call with its arguments on our spy
object. This is something thah we can assert by (3).spy
could fail. class TestUnderTest < Minitest::Test
def test_get_request
open_spy = Spy.on_instance_method(Kernel, :open)
.and_return { OpenStruct.new(read: "whatever") }
UnderTest.new.get_request("http://google.com")
assert open_spy.has_been_called_with?("http://yahoo.com")
end
end
1) Failure:
TestUnderTest#test_get_request [test/lib/test_under_test.rb:17]:
Failed assertion, no message given.
get_request
, with "http://google.com", but asserting if spy
registered call with "http://yahoo.com" argument.spy
works as expected.