Minitest:如何 stub/mock Kernel.open 在 URL 上的文件结果
Minitest: How to stub/mock the file result of Kernel.open on a URL
我一直在尝试使用 Minitest 来测试我的代码 (full repo),但我在使用一种方法时遇到了问题,该方法从网站上的 .txt 文件下载 SHA1 散列,returns值。
方法:
def download_remote_sha1
@log.info('Downloading Elasticsearch SHA1.')
@remote_sha1 = ''
Kernel.open(@verify_url) do |file|
@remote_sha1 = file.read
end
@remote_sha1 = @remote_sha1.split(/\s\s/)[0]
@remote_sha1
end
你可以看到我记录了命令行发生的事情,创建了一个对象来保存我的 SHA1 值,打开 url(例如 https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.2.deb.sha1.txt)
然后我拆分字符串,以便我只有 SHA1 值。
问题是在测试期间,我想存根 Kernel.open
,它使用 OpenURI 打开 URL。我想确保我实际上并没有伸出手去下载任何文件,而是我只是传递块我自己的模拟 IO 对象测试它是否正确拆分了东西。
我尝试像下面的块一样,但是当 @remote_sha1 = file.read
出现时,文件项为零。
@mock_file = Minitest::Mock.new
@mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub :open, @mock_file do
@downloader = ElasticsearchUpdate::Downloader.new(hash, true)
@downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
stub
的第二个参数是您希望 return 值 在测试期间的值,但是 Kernel.open
在这里使用需要它产生给要更改的块的值。
您可以通过提供第三个参数来实现。尝试将对 Kernel.stub
的调用更改为
Kernel.stub :open, true, @mock_file do
#...
注意额外的参数 true
,因此 @mock_file
现在是第三个参数,将被生成块。在这种情况下,第二个参数的实际值并不重要,您可能也想在那里使用 @mock_file
以更接近地对应 open
的行为方式。
我也在研究这个问题,但 matt
先弄明白了。添加到 matt
发布的内容:
当你写:
Kernel.stub(:open, @mock_file) do
#block code
end
...这意味着当 Kernel.open()
被调用时——在 any 代码中,anywhere 在 stub() 之前块结束——Kernel.open()
的 return 值将是 @mock_file。但是,您永远不会在代码中使用 Kernel.open() 的 return 值:
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
如果你想使用 Kernel.open() 的 return 值,你必须这样写:
return_val = Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
#do something with return_val
因此,Kernel.open() 的 return 值与您的代码无关——这意味着 stub() 的第二个参数无关。
仔细检查 source code for stub() 发现 stub() 接受第三个参数——一个参数将被传递给在 stubbed 方法之后指定的 block称呼。实际上,您在存根 Kernel.open() 方法调用后指定了一个块:
stubbed method call -+ +- start of block
| | |
V V V
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
^
|
end of block
因此,为了将 @mockfile 传递给块,您需要将其指定为 Kernel.stub() 的第三个参数:
Kernel.stub(:open, 'irrelevant', @mock_file) do
end
以下是供未来搜索者使用的完整示例:
require 'minitest/autorun'
class Dog
def initialize
@verify_url = 'http://www.google.com'
end
def download_remote_sha1
@remote_sha1 = ''
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
#puts @remote_sha1[0..300]
@remote_sha1 = @remote_sha1.split(" ")[0] #Using a single space for the split() pattern will split on contiguous whitespace.
end
end
#Dog.new.download_remote_sha1
describe 'downloaded file' do
it 'should be an sha1 code' do
@mock_file = Minitest::Mock.new
@mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub(:open, 'irrelevant', @mock_file) do
@downloader = Dog.new
@downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
end
end
xxx
我一直在尝试使用 Minitest 来测试我的代码 (full repo),但我在使用一种方法时遇到了问题,该方法从网站上的 .txt 文件下载 SHA1 散列,returns值。
方法:
def download_remote_sha1
@log.info('Downloading Elasticsearch SHA1.')
@remote_sha1 = ''
Kernel.open(@verify_url) do |file|
@remote_sha1 = file.read
end
@remote_sha1 = @remote_sha1.split(/\s\s/)[0]
@remote_sha1
end
你可以看到我记录了命令行发生的事情,创建了一个对象来保存我的 SHA1 值,打开 url(例如 https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.2.deb.sha1.txt)
然后我拆分字符串,以便我只有 SHA1 值。
问题是在测试期间,我想存根 Kernel.open
,它使用 OpenURI 打开 URL。我想确保我实际上并没有伸出手去下载任何文件,而是我只是传递块我自己的模拟 IO 对象测试它是否正确拆分了东西。
我尝试像下面的块一样,但是当 @remote_sha1 = file.read
出现时,文件项为零。
@mock_file = Minitest::Mock.new
@mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub :open, @mock_file do
@downloader = ElasticsearchUpdate::Downloader.new(hash, true)
@downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
stub
的第二个参数是您希望 return 值 在测试期间的值,但是 Kernel.open
在这里使用需要它产生给要更改的块的值。
您可以通过提供第三个参数来实现。尝试将对 Kernel.stub
的调用更改为
Kernel.stub :open, true, @mock_file do
#...
注意额外的参数 true
,因此 @mock_file
现在是第三个参数,将被生成块。在这种情况下,第二个参数的实际值并不重要,您可能也想在那里使用 @mock_file
以更接近地对应 open
的行为方式。
我也在研究这个问题,但 matt
先弄明白了。添加到 matt
发布的内容:
当你写:
Kernel.stub(:open, @mock_file) do
#block code
end
...这意味着当 Kernel.open()
被调用时——在 any 代码中,anywhere 在 stub() 之前块结束——Kernel.open()
的 return 值将是 @mock_file。但是,您永远不会在代码中使用 Kernel.open() 的 return 值:
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
如果你想使用 Kernel.open() 的 return 值,你必须这样写:
return_val = Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
#do something with return_val
因此,Kernel.open() 的 return 值与您的代码无关——这意味着 stub() 的第二个参数无关。
仔细检查 source code for stub() 发现 stub() 接受第三个参数——一个参数将被传递给在 stubbed 方法之后指定的 block称呼。实际上,您在存根 Kernel.open() 方法调用后指定了一个块:
stubbed method call -+ +- start of block
| | |
V V V
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
^
|
end of block
因此,为了将 @mockfile 传递给块,您需要将其指定为 Kernel.stub() 的第三个参数:
Kernel.stub(:open, 'irrelevant', @mock_file) do
end
以下是供未来搜索者使用的完整示例:
require 'minitest/autorun'
class Dog
def initialize
@verify_url = 'http://www.google.com'
end
def download_remote_sha1
@remote_sha1 = ''
Kernel.open(@verify_url) do |f|
@remote_sha1 = f.read
end
#puts @remote_sha1[0..300]
@remote_sha1 = @remote_sha1.split(" ")[0] #Using a single space for the split() pattern will split on contiguous whitespace.
end
end
#Dog.new.download_remote_sha1
describe 'downloaded file' do
it 'should be an sha1 code' do
@mock_file = Minitest::Mock.new
@mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub(:open, 'irrelevant', @mock_file) do
@downloader = Dog.new
@downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
end
end
xxx