← Back to Upcase

Rspec faking the filesystem?


(Russen Guggemos) #1

I’m trying to write some coverage for a class that writes data to a temp file, then returns it once complete.

My tests pass fine locally, but our testing service, Codeship, hates it when I try to use the filesystem, and fails the test.

I started off with FakeFS, but quickly discovered that it seems stale for Ruby 1.9. Things like “File.open(…) do …” don’t work, etc.

Is there anything else to remove the dependency on a filesystem, or am I down to stub.and_yield setups for the File library?

0.upto(large_number_of_batches) do |page|
    records.by_page(page)
    File.open(tmp_file_path, 'a') do |f|
        records.each do |r|
            f.write r.to_csv
        end
    end
end

File.open(tmp_file_path, 'r') { |f| f.read }

@joshclayton, what do you think?


(Jon Yurek) #2

What is the purpose of the code? Is it important that the data be written to disk, or is that just how the implementation happens to go? That is, can it be abstracted in a way that the method you’re testing doesn’t really care? That would make it more of a black box that can be abstracted away during the tests.


(Russen Guggemos) #3

Instead of a caching layer, we’re using the disk as a temporary storage while rendering a CSV of 300,000 lines. Let’s assume that this is necessary, for the minute.

After the TDD Rails workshop, I got inspired to write a fake for the filesystem, rather than messing with stub/and_yield stuff:

  class FakeFile
    @@class_contents = {}

    def self.open(path, flags)
      @@current_path = path
      yield self
    end

    def self.write line
      @@class_contents[@@current_path] ||= []
      @@class_contents[@@current_path] << line
    end

    def self.read
      @@class_contents[@@current_path].join
    end
  end

It doesn’t do everything a File class might, but it has enough methods to pretend to be a caching layer inside of my test.


(Eric Marthinsen) #4

I follow the wisdom of “only mock what you own.” In your case, I think your best bet would be to create a class like DiskCache that abstracts the file system away from your application. In your tests, you can stub the methods on DiskCache or provide an alternate implementation to whatever code is supposed to call it. Separately, write some tests for DiskCache that actually writes to the filesystem. This gives you the benefit of code that is completely under test and provides some nice hooks for your stubs. If you are trying to avoid the stub/and_yield stuff (which is hairy), then creating a FakeDiskCache for testing is probably the way to go.