Unit Testing Methods With File IO

15,876

Solution 1

Regarding how to test File I/O: A common way of doing this is encapsulating File I/O in a wrapper class. For example:

public class FileHandler : IFileHandler
{
     public string GetFilename(string name)
     {
         return System.IO.GetFileName(name);
     }

    public string GetDirectoryName(string directory)
    {
         return System.IO.GetDirectoryName(directory);
    }
}

public interface IFileHandler
{
      string GetFilename(string name);
      string GetDirectoryName(string directory);
}

In your method under test, you only program against the interface. When you run your unit test, you would use a mock object which returns predefined values. For this, you could use Moq.

This is a bit work to do but then you do not need to test File I/O as well.

Best Jan

Solution 2

How can i test a method using IO?

By introducing abstraction over the .NET's IO libraries and then using real implementation (BCL) in application, and fake (mocked) one in unit test. Such abstraction is fairly simple to write on your own, but those projects already exist (eg. System.IO.Abstraction) so reinventing wheel is pointless, especialy in such trivial matter.

I don't really want to be using a real file as this would be slow

This is correct, in test you want to use fake (mock) objects.

Is a project like this worth unit testing?

Every project is worth testing and you will test it in one way or another (in most extreme case, by manually executing application and doing good, old click-and-wait testing).

Note that almost always it is too time consuming to manually test 10 edge cases of complex parser method which everytime requires you to load large file and wait for results. This is where automated tests in isolated environment help greatly. It is the same with your code - file system, web service, they all introduce large overhead before you can get to actual business logic. Must isolate.

A lot of the methods in this project are private (...) I've read you should only really test methods which are externally visible

Correct again, you should be testing public contract. However, if private stuff gets big enough to test it, most of the times it's a good indicator it is also big enough to put it in its own class. Note that changing members access modifiers only for the unit test usage is not the best idea. Almost always it's better to extract-class/method refactor.

Solution 3

You can do that using Microsoft Fakes. First generate a fake assembly for System.dll - or any other package and then mock expected returns as in:

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimDirectory.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.MoveStringString = (p,n) => {};
     // .. and other methods you'd like to test

     var result = RestoreExtension("C:\myfile.ext");
     // then you can assert with this result 
}
Share:
15,876
rupertmulrenan
Author by

rupertmulrenan

Updated on June 04, 2022

Comments

  • rupertmulrenan
    rupertmulrenan about 2 years

    I'm trying to get into the habit of writing unit tests, I've written a few before but they've usually been quite basic...I'd like to start making a move to TDD as i want to improve the quality of my code (design and structure) - reducing coupling, while at the same time hopefully reduce number of regressions which slip through to a testable build.

    I have taken a relatively simple project i work on to begin with. The resultant program watches a folder and then acts on files within this folder.

    Here is a typical example of some code extracted from the project:

    private string RestoreExtension(String file)
        {
            var unknownFile = Path.GetFileName(file);
            var ignoreDir = Path.GetDirectoryName(file) + "\\Unknown";
            string newFile;
    
            //We need this library for determining mime
            if (CheckLibrary("urlmon.dll"))
            {
                AddLogLine("Attempting to restore file extension");
                var mime = GetMimeType(BufferFile(file, 256));
                var extension = FileExtensions.FindExtension(mime);
                if (!String.IsNullOrEmpty(extension))
                {
                    AddLogLine("Found extension: " + extension);
                    newFile = file + "." + extension;
                    File.Move(file, newFile);
                    return newFile;
                }
            }
            else
            {
                AddLogLine("Unable to load urlmon.dll");
            }
    
            AddLogLine("Unable to restore file extension");
    
            if (!Directory.Exists(ignoreDir))
            {
                Directory.CreateDirectory(ignoreDir);
            }
            var time = DateTime.Now;
            const string format = "dd-MM-yyyy HH-mm-ss";
    
            newFile = ignoreDir + "\\" + unknownFile + "-" + time.ToString(format);
            File.Move(file, newFile);
    
            return String.Empty;
        }
    

    Questions :

    How can i test a method using IO? I don't really want to be using a real file as this would be slow (and more of an integration test?), but i cant really see another way. I could add a switchable IO abstraction layer, but this sounds to me like it may unnecessarily complicate code...

    Is a project like this worth unit testing? By that i mean, is it too simple. Once you strip out .Net and 3rd party library calls there is not much left...So is it a case that the amount of work to make it testable means its not a good candidate to test?

    A lot of the methods in this project are private, as this project happens to be a windows service. I've read you should only really test methods which are externally visible (public or exposed through an interface), it is unlikely in this case however that i want to expose any of my own methods. So is this project worth testing (as id probably need to either change method access modifiers to public or add some attributes so that NUnit can see them)?