Question

I am a novice in Unit and Integration testing. I have a class which contains a few methods for deleting multiple files in a specified directory and moves files to a different directory. In total there are 3 methods. Full class code:

    public class FilesUtility : IFilesUtility
{
    public void TidyUpXmlFiles(bool isDebug, string XmlFileDirectory)
    {
        if (isDebug)
        {
            MoveXmlFiles(XmlFileDirectory);
        }
        else
        {
            DeleteXmlFiles(XmlFileDirectory);
        }
    }

    private static void MoveXmlFiles(string XmlFileDirectory)
    {
        var di = new DirectoryInfo(XmlFileDirectory);
        // Create a subdirectory in the parent directory called XmlArchive.
        DirectoryInfo destinationDirectory = di.CreateSubdirectory("XmlArchive");
        string destinationDir = destinationDirectory.ToString();
        if (!Directory.Exists(destinationDir))
        {
            Directory.CreateDirectory(destinationDir);
        }
        foreach (string file in Directory.GetFiles(XmlFileDirectory,
                                                                "*.Xml"))
        {
            // Use static Path method to extract only the file name from the path.
            string fileName = Path.GetFileName(file);
            if (fileName != null)
            {
                string destFile = Path.Combine(destinationDir, fileName);
                //if the same file exists in the destination folder, delete the file and move the new file.
                if (File.Exists(file))
                {
                    File.Delete(destFile);
                    File.Move(file, destFile);
                }
            }
        }
    }

    private static void DeleteXmlFiles(string XmlFileDirectory)
    {
        foreach (var file in Directory.GetFiles(XmlFileDirectory,
                                                                "*.Xml"))
        {
            File.Delete(file);
        }
    }

    public void MoveFaultyXml(string XmlFileDirectory, string fileFullPath)
    {
        var di = new DirectoryInfo(XmlFileDirectory);
        // Create a subdirectory in the parent directory called XmlArchive.
        var destinationDirectory = di.CreateSubdirectory("FaultyXml");
        var faultyXmlFileName = Path.GetFileName(fileFullPath);
        var destinationDir = destinationDirectory.ToString();
        if (!Directory.Exists(destinationDir))
        {
            Directory.CreateDirectory(destinationDir);
        }

        if (faultyXmlFileName != null)
        {
            string destFile = Path.Combine(destinationDir, faultyXmlFileName);
            //if the same file exists in the destination folder, delete the file and move the new file.
            if (!File.Exists(fileFullPath)) return;
            File.Delete(destFile);
            File.Move(fileFullPath, destFile);
        }
    }

I have created an Interface to this method to use Moq to Mock if that is what I need to do I am not sure. The Interface code is:

public interface IFilesUtility
{
    void TidyUpXbrlFiles(bool isDebug, string xbrlFileDirectory);
    void MoveFaultyXbrl(string xbrlFileDirectory, string fileFullPath);
}

Now I want to be able to test the above methods to make sure that they can actually pick up a list of XML files from a directory and delete/move all of them. I have been reading about Moq and mocking. I have also been reading about Dependency injection but my problem is I have been reading too much on it and now can't think of where to start.

I am hoping somebody can explain to me using the example code posted above how i can construct a sucessful test for this code. I have read so many questions about this on Stackoverflow and I hope you will not close this as a duplicate because like I said my problem is I have been reading too much about this. I think I might understand it if somebody could explain it to me using one of the methods in my example.

I don't know how to to do a correct [TestFixtureSetup] which I think I will need in my case where i can get a list of files to use on each test.

Thanks to all that take time to answer :)

Was it helpful?

Solution

Here's what I do to test file-handling methods:

  1. Create the file on the local system. Sometimes I create these manually in the MasterFiles folder and then commit them as part of the source then call UseTestMasterFile to copy it into the test folder. Sometimes I create them within the test itself and save them into the test folder.
  2. Run my test.
  3. Clean up the test files.

Here's the test code...

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using System.IO;

public class FilesUtilityTests {
    [Test]
    public void TidyUpXmlFilesDeletesXmlFilesWhenNotDebugging() {
        string testFilePath = TestFileHelper.UseTestMasterFile("YourXmlFile.xml");
        FilesUtility util = new FilesUtility();

        util.TidyUpXmlFiles(false, TestFileHelper.TestDirectoryName);

        Assert.IsFalse(File.Exists(testFilePath));
        // any other tests here.
    }
}

// Obviously, this could be in a separate dll that you reference on all test projects.
public static class TestFileHelper {
    public const string TestFolderName = @"Testing\UnitTests";
    public const string DefaultMasterFilesFolderName = "MasterFiles";

    public static string DefaultTestDirectoryName {
        get { return Path.Combine(Path.GetDirectoryName(System.Environment.CurrentDirectory), TestFolderName); }
    }
    public static string TestDirectoryName {
        get { return testDirectoryName ?? DefaultTestDirectoryName; }
        set { testDirectoryName = value; }
    }
    private static string testDirectoryName;

    public static string MasterFilesFolderName {
        get { return masterFilesFolderName ?? DefaultMasterFilesFolderName; }
        set { masterFilesFolderName = value; }
    }
    private static string masterFilesFolderName;

    public static string TestFileExtension { get; set; }

    public static string BuildTestFileName(string fileName) {
        if (String.IsNullOrWhiteSpace(Path.GetPathRoot(fileName)))
            fileName = Path.Combine(TestDirectoryName, fileName);
        if (String.IsNullOrEmpty(Path.GetExtension(fileName)))
            fileName = Path.ChangeExtension(fileName, TestFileExtension);
        return fileName;
    }

    public static string BuildTestMasterFileName(string fileName) {
        if (Path.IsPathRooted(fileName))
            return Path.Combine(Path.Combine(Path.GetDirectoryName(fileName), MasterFilesFolderName), Path.GetFileName(fileName));
        else
            return Path.Combine(Path.Combine(TestDirectoryName, MasterFilesFolderName), fileName);
    }

    public static string UseTestMasterFile(string fileName) {
        string dest = BuildTestFileName(fileName);
        string source = BuildTestMasterFileName(dest);
        DeleteTestFile(dest);
        ClearReadOnlyAttributes(source);
        File.Copy(source, dest, true);
        return dest;
    }

    public static void DeleteTestFile(string filePath) {
        if (String.IsNullOrWhiteSpace(filePath)) { return; }
        if (!File.Exists(filePath)) { return; }

        ClearReadOnlyAttributes(filePath);
        File.Delete(filePath);
    }

    public static void ClearReadOnlyAttributes(string filePath) {
        if (String.IsNullOrWhiteSpace(filePath)) { return; }
        if (!File.Exists(filePath)) { return; }

        FileAttributes attributes = File.GetAttributes(filePath);
        if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            File.SetAttributes(filePath, attributes ^ FileAttributes.ReadOnly);
    }
    public static void SetReadOnlyAttributes(string filePath) {
        if (String.IsNullOrWhiteSpace(filePath)) { return; }
        if (!File.Exists(filePath)) { return; }

        FileAttributes attributes = File.GetAttributes(filePath);
        if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            File.SetAttributes(filePath, attributes | FileAttributes.ReadOnly);
    }
}

OTHER TIPS

Expanding on the comment that Maess left, generally you do not want to test things that handle file input/output, even in an integration test. The reason is that is the responsibility of the file system, and read/write permissions get involved, plus you'll hammer the drive.

If you absolutely must test that you can delete them, you'll definitely want to do that in an integration test, and you do NOT want to run this thing very often.

Generally, Unit tests are designed to be small, fast, independent, and ran on just about every change. They should generally be isolated to testing your code, not others (like System.IO).

In order to unit test your code, you'll need to mock against the IFilesUtility inteface, and pass this in as a collaborator to the class that would be calling the TidyUp or Move functions.

Then you can verify that these are called, and I think this will provide a lot more value than testing whether or not the system is able to delete files.

Something like

public void WhenDoingActionXthenTidyUpIsCalled(){

  var filesUtil = new Mock<IFilesUtility>();
  var sut = new YourClassUnderTest( filesUtil.object );


  sut.DoSomethingThatTriggersTidyUp();


  filesUtil.verify( f => f.TidyUp(), Times.Once());
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top