Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing Methods With File IO

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)?

like image 302
rupertmulrenan Avatar asked May 14 '13 11:05

rupertmulrenan


People also ask

Which method is used for unit testing?

Unit tests can be performed manually or automated. Those employing a manual method may have an instinctual document made detailing each step in the process; however, automated testing is the more common method to unit tests. Automated approaches commonly use a testing framework to develop test cases.

What is unit testing in test file?

Unit Testing is a type of software testing where individual units or components of a software are tested. The purpose is to validate that each unit of the software code performs as expected. Unit Testing is done during the development (coding phase) of an application by the developers.

How do you write unit test cases for file upload?

Ways to write unit test code for File upload on Controllers: For file upload related testing we need to Mock HTTPContext, HTTPContext. Server and HttpPostedFileBase. These classes handle the file uploading and saving.

How do I run a unit test file?

To run all the tests in a default group, choose the Run icon and then choose the group on the menu. Select the individual tests that you want to run, open the right-click menu for a selected test and then choose Run Selected Tests (or press Ctrl + R, T).


2 Answers

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

like image 79
nein. Avatar answered Sep 17 '22 14:09

nein.


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.

like image 20
k.m Avatar answered Sep 19 '22 14:09

k.m