Stubbing File.open with Rspec

青春壹個敷衍的年華 提交于 2019-12-23 17:53:05

问题


I'm attempting to stub File.open in order to test a method I have that reads a CSV file.

Here's the model:

class BatchTask
  def import(filename)
    CSV.read(filename, :row_sep => "\r", :col_sep => ",")
  end
end

Here's the spec code:

let(:data) { "title\tsurname\tfirstname\rtitle2\tsurname2\tfirstname2\r"}
let(:result) {[["title","surname","firstname"],["title2","surname2","firstname2"]] }

it "should parse file contents and return a result" do
  File.stub(:open).with("file_name","rb") { StringIO.new(data) }
  person.import("file_name").should == result
end

However, when I attempt to do this I get (stacktrace):

Errno::ENOENT in 'BatchTask should parse file contents and return a result'
No such file or directory - file_name
/Users/me/app/models/batch_task.rb:4:in `import'
./spec/models/batch_task_spec.rb:10:

Finished in 0.006032 seconds

I've been banging my head against this one and can't figure out what I'm doing wrong. Any help would be greatly appreciated!


回答1:


It would be helpful to provide a stacktrace, although I will make a guess why it happens. Also, I believe you're approach in here is not good and I will elaborate on how I believe you should test.

Simply, I think that CSV.read does not use File.open. It can use Kernel#open or various other ways a file can be opened in Ruby. You should not stub File.open in a test as this anyway.

There is a great book called Growing Object-Oriented Software Guided by Tests that has a need rule:

Stub only methods on classes/interfaces you control

There is a very simple reason for that. When you're doing test doubles (stub), the primary reason is interface discovery - you want to figure out how the interface of your class should look like and doubles provide you with neat feedback. There is also a secondary reason - stubbing external libraries tends to be quite tricky in some cases (when the library is not extremely stubbable). Thus, you can take a few different approaches here, that I will enumerate:

  1. You can test in integration. You can either create the file in each test and pass the pathname (which is fine).
  2. You can break down the way you're parsing. Instead of passing a filename to CSV.read, find a way when you pass an open File and then stub that in the test. i.e., have your code open the file instead of CSV. That way you can stub it easily
  3. Stub CSV.read instead. That might be a bit dramatic, but in essence, you're not testing your code, you're testing the CSV library. It should already have its own tests and you don't need to test it anyway. Instead, you can rely on the fact that it works and just stub the call to it.

Out of those, I would probably go with the third one. I don't like testing dependencies in my unit tests. But if you want your test to call that code, I suggest finding a way to do the second option (CSV.new(file) should do the trick, but I don't have time to investigate) and finally fall back to #1 if nothing else works.



来源:https://stackoverflow.com/questions/11691722/stubbing-file-open-with-rspec

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!