2

我有这个方法:

def download_zip_file
  FileUtils.rm_rf(@zip_path) if @zip_path
  FileUtils.mkdir_p(@zip_path)
  downloaded_file = File.open(@zip_file, "wb")
  request = Typhoeus::Request.new(@feed, followlocation: true)
  request.on_body { |chunk| downloaded_file.write(chunk) }
  request.on_complete { |response| downloaded_file.close }
  request.run
end

它清除 zip_path,重新创建它,打开一个文件进行写入,然后从@feedURL 下载文件并以块的形式写入下载的文件。

我想知道如何对其进行单元测试,模拟实际的请求。由于它通过一些块使用分块,所以有点复杂。

我以前有这个代码:

def download_feed_data_from_url
  response = Typhoeus.get(@feed, followlocation: true)
  raise(FeedNotFoundError, "Could not find feed at feed: #{@feed}. Response: #{response.inspect}") unless response.success?
  result = response.body
end

这很容易测试(通过模拟 Typhoeus 并提供存根返回):

context "testing feed downloading" do
  let(:feed) { "http://the.feed.url" }
  let(:response) { double(body: "some content", success?: true) }

  before
    allow(Typhoeus).to receive(:get).with(feed, followlocation:true).and_return(response)
  end

  # ... individual assertions, i.e. that Typhoeus is called, that it pulls the body content, etc.
end

所以我想知道如何对相同种类的东西进行单元测试......即在模拟 Typhoeus 时创建路径、保存文件等。由于它是一个 3rd 方库,我不需要测试它是否有效,只要它被正确调用即可。

这是分块,on_bodyon_complete让我感到困惑(就如何测试而言)

4

2 回答 2

4

您的方法的主要职责是将 zip 文件下载到本地文件系统中的特定文件夹中。您如何执行此操作并不重要。结果在这里很重要。

如果您想检查您的方法是否正确下载,那么您应该存根网络请求,然后调用download_zip_file方法,然后检查是否在相应路径创建了文件,并且其内容是否与存根响应正文匹配。

Typhoeus 支持请求存根:https ://github.com/typhoeus/typhoeus/tree/d9e6dce92a04754a2276c94393dad0f3a5c06bdd#direct-stubbing 或者,您可以将 Webmock 用于相同目的。它支持 Typhoeus 请求存根:https ://github.com/bblimke/webmock

例子:

it "downloads file" do
  zip_path = "tmp/downloads"
  zip_file = "filename.zip"
  downloaded_file_path = "#{zip_path}/#{zip_file}"
  feed = "http://www.colorado.edu/conflict/peace/download/peace.zip"
  zip_file_content = "some zip file content"

  response = Typhoeus::Response.new(code: 200, body: zip_file_content)
  Typhoeus.stub(feed).and_return(response)

  Downloader.new(zip_path, zip_file, feed).download_zip_file

  expect(File.exists?(downloaded_file_path)).to eq(true)
  expect(File.read(downloaded_file_path)).to eq(zip_file_content)
end

此外,我建议在您的测试中使用内存中的假文件系统,它会创建文件和文件夹,以免垃圾污染本地文件系统。Memfs 是一个很好的宝石。https://github.com/simonc/memfs 很容易将它添加到您的测试中:

before do
  MemFs.activate!
end

after do
  MemFs.deactivate!
end

之后,您的测试将不会创建任何本地文件,但功能将保持不变。

于 2016-08-03T13:16:19.463 回答
3

The key is that you can give an RSpec allow or expect a block implementation. So stub on_body and on_complete to save the blocks you give them, and run to call those blocks:

it "writes to and closes the file" do
  downloaded_file = double
  expect(downloaded_file).to receive(:write)
  expect(downloaded_file).to receive(:close)
  allow(File).to receive(:open).and_return(downloaded_file)

  request = double
  allow(request).to receive(:on_body) { |&block| @on_body = block }
  allow(request).to receive(:on_complete) { |&block| @on_complete = block }
  allow(request).to receive(:run) do
    @on_body.call "chunk"
    @on_complete.call nil
  end
  allow(Typhoeus::Request).to receive(:new).and_return(request)

  download_zip_file
end

I didn't verify any arguments, but you can add withs to do that. I also left out stubbing and mocking the FileUtils calls, since those are easy.

The interesting part of this question is how to stub an API like this in RSpec, so that's what I addressed. However, I would probably write an acceptance test (Cucumber or RSpec feature spec) first that exercises all of the code end to end, then write unit tests to test error handling and such.

于 2016-08-03T12:40:21.090 回答