File system mocking

is it really possible to use classes and interfaces to mock file system without libraries in crystal ?

It is possible to monkey-patch File and Dir for your use-case, but if this is for specs then you might find testing with direct IO inputs would be easier then mocking the file system. For example, take the following method:

def reverse_contents(file : File) : Nil
  content = file.gets_to_end.reverse
  file.rewind << content
end

This method doesn’t need to have access to a file directly, it can instead use IO which allows you to do this:

require "spec"

it "reverses contents" do
  io = IO::Memory.new
  io << "foo bar baz"
  io.rewind
  reverse_contents io

  io.to_s.should eq "zab rab oof"
end

The short answer is yes. Totally possible for you to do something like:

module FileSystemInterface
  abstract def write(path : String | Path, content : String) : Nil
  abstract def read(path : String | Path) : String
end

class LocalFileSystem
  include FileSystemInterface

  def write(path : String | Path, content : String) : Nil
    File.write path, content
  end

  def read(path : String | Path) : String
    File.read path
  end
end

class MockFileSystem
  include FileSystemInterface

  @files = Hash(String, String).new

  def write(path : String | Path, content : String) : Nil
    @files[path.to_s] = content
  end

  def read(path : String | Path) : String
    @files[path.to_s]? || raise "File does not exist"
  end
end

Then your logic can depend upon FileSystemInterface, defaulting to LocalFileSystem, but using MockFileSystem in specs.

this works with file extensions ? i want to obfuscate the file extension i want to make pdf looks like txt but actually it is txt is it possible with classes and interfaces ?

If that’s what you mean by “filesystem mocking” then I’m not sure I follow why you think you need classes and interfaces. If you just want to save a PDF with a .txt file extension then you can just do that when you save the file.