- Write Integration tests, not unit tests. For this to work you need a simple way of creating a folder where you can dump stuff without worrying about other tests interfering. I have a simple TestFolder (see below) class which can create a unique per test method folder to use.
- Write a mockable System.IO.File. That is create a IFile.cs. I find using this often ends up with tests that simply prove you can write mocking statements, but do use it when the IO usage is small.
- Examine you layer of abstraction, and extract the file IO from the class. The create a interface for this. The remainder use integration tests (but this will be very small). This differs from above in that instead of doing file.Read you write the intent, say ioThingie.loadSettings()
- System.IO.Abstractions. I've not used this yet, but it is the one I'm most excited about playing with.
I end up using all the methods above, depending on what I'm writing. But most of the time I end up thinking abstraction is wrong when I write unit tests that hit the IO.
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace IHazTehCodez.Common.Test.IO
{
///
/// Generates a folder to use for intergration tests. The returned folder is unique pre test method.
///
public class TestFolder
{
///
/// By default test folders are created in %TEMP%\tests. This ctor allows that to be changed, for
/// example to run the same test concurently with each instance having a different root could be used .
///
///
The root folder.
public TestFolder(string root)
{
if (root.EndsWith("\\"))
{
Root = root;
} else
{
Root = root + "\\";
}
}
///
/// Generates test folders using the default %TEMP%\tests.
///
public TestFolder()
: this(Path.GetTempPath() + "\\tests\\")
{
}
///
/// The root folder, off which all other test folders are generated.
///
public string Root { get; private set; }
internal string Folder()
{
StackFrame[] stack = (new StackTrace()).GetFrames();
ValidateStackFrames(stack);
foreach (var stackFrame in stack)
{
MethodBase method = stackFrame.GetMethod();
if (IsTestMethod(method))
{
return Root + method.ReflectedType.Namespace + "." +
method.ReflectedType.Name + "\\" +
method.Name;
}
}
throw new ArgumentException("Not called within test framework.");
}
private static void ValidateStackFrames(StackFrame[] stack)
{
if(stack == null)
{
throw new ArgumentException("Not called within test framework.");
}
}
private static bool IsTestMethod(MethodBase method)
{
var attributes = method.GetCustomAttributes(typeof(TestMethodAttribute), false) as TestMethodAttribute[];
return attributes != null && attributes.Length > 0;
}
///
/// Returns a folder unit to a running Unit Test. The same folder will be returned no matter how ofton it is
/// called from within that test.
///
///
///
///
public string Create()
{
string folder = Folder();
try
{
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
}
Directory.CreateDirectory(folder);
}catch(Exception e)
{
Assert.Inconclusive("Unable to build test folder " + folder + ", failed with " + e.Message);
}
return folder;
}
}
}

0 comments:
Post a Comment