Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit-test saving file to the disk?

Tags:

I know that it's strongly recommended to run unit-tests in separation from file system, because if you do touch file system in your test, you also test file system itself. OK, that's reasonable.
My question is, if I want to test file saving to the disk, what do I do? As with database, I separate an interface that is responsible for database access, and then create another implementation of this for my tests? Or may be there's some other way?

like image 267
chester89 Avatar asked Aug 01 '10 11:08

chester89


People also ask

Should unit tests write files?

There is a general rule to be cautious of writing unit tests that do file I/O, because they tend to be too slow. But there is no absolute prohibition on file I/O in unit tests. In your unit tests have a temporary directory set up and torn down, and create test files and directories within that temporary directory.

Where do I put test files?

By convention, Go testing files are always located in the same folder, or package, where the code they are testing resides. These files are not built by the compiler when you run the go build command, so you needn't worry about them ending up in deployments.

How do you run unit test and record its results?

The tester must determine from the results whether the unit has passed or failed the test. If the test is failed, the nature of the problem should be recorded in what is sometimes called a test incident report (see Chapter 7). Differences from expected behavior should be described in detail.


2 Answers

My approach towards this is heavily biased on the Growing Object-Oriented Software Guided by Tests (GOOS) book that I just read, but it's the best that I know of today. Specifically:

  • Create an interface to abstract away the file system from your code. Mock it where this class is needed as a collaborator/dependency. This keeps your unit-tests quick and feedback fast.
  • Create integration tests that test the actual implementation of the interface. i.e. verify that calling Save() actually persists a file to disk and has the write contents (use a reference file or parse it for a few things that it should contain)
  • Create an acceptance test that tests the whole system - end to end. Here you may just verify that a file is created - the intent of this test is to confirm if the real implementation is wired / plugged in correctly.

Update for commenter:

If you're reading structured data (e.g. Book objects) (If not substitute string for IEnumerable)

interface BookRepository {   IEnumerable<Books> LoadFrom(string filePath);   void SaveTo(string filePath, IEnumerable<Books> books); } 

Now you can use constructor-injection to inject a mock into the client class. The client class unit tests therefore are fast ; do not hit the filesystem. They just verify that the right methods are called on the dependencies (e.g. Load/Save)

var testSubject = new Client(new Mock<BookRepository>.Object); 

Next you need to create the real implementation of BookRepository that works off a File (or a Sql DB tommorrow if you want it). No one else has to know. Write integration tests for FileBasedBookRepository (that implements the above Role) and test that calling Load with a reference file gives the right objects and calling Save with a known list, persists them to the disk. i.e. uses real files These tests would be slow so mark them up with a tag or move it to a separate suite.

[TestFixture] [Category("Integration - Slow")] public class FileBasedBookRepository  {   [Test]   public void CanLoadBooksFromFileOnDisk() {...}   [Test]   public void CanWriteBooksToFileOnDisk() {...} } 

Finally there should be one/more acceptance tests that exercises Load and Save.

like image 164
Gishu Avatar answered Sep 25 '22 18:09

Gishu


You could instead of passing a filename to your save function, pass a Stream, TextWriter or similar. Then when testing you can pass a memory-based implementation and verify the correct bytes are written without actually writing anything to disk.

To test problems and exceptions you could take a look at a mocking framework. This can help you to artifically generate a specific exception at the certain point in the save process and test that your code handles it appropriately.

like image 20
Mark Byers Avatar answered Sep 23 '22 18:09

Mark Byers