25. August 2008 00:07 by David in
After moving to England I quickly found that I was pining for a library of test utility classes that a colleague of mine had written while we were both consulting on a large ETL project. I will be slowly rewriting the classes and placing the code on PebbleSteps. The first class I will be discussing is the TemporaryDirectory which allows the developer to dump files into a folder which gets automatically deleted when the test completes.
For a quick recap the best unit tests:
- Are fast
- Are rerunnable
- Don't leave a mess
Slow unit tests are inevitably moved into the nightly build process. This implies that the last two unit test attributes are the most important. Problems arise when the unit tests need to interact with resources. Each time the unit test leaves the happy world of burning CPU cycles and moves into interacting with other systems traces of the interaction are left. Log files are created, alerts are generated, database tables are changed. Luckily many resources implement the resource manager APIs and all the modifications disappear when the transaction rolls back. Unfortunately there are a number of scenarios where it is impossible to place the entire test inside a transaction.
These include:
- Interacting with files
- Creating databases
- Interacting with systems that manage their own transactions such as SQL Server Integration Server
When testing a component that interacts with files it is important that the unit test cleans up the files after the test completes. The TemporaryDirectory class automatically creates a directory in its constructor and removes the directory when the Dispose method is called.
[Test]
public void Market_data_file_validator()
{
const string BloombergSecurityDataResource = "Bloomberg.Security.txt";
using (TemporaryDirectory tempDir = new TemporaryDirectory())
{
tempDir.StoreToDirectory
(this.GetType().Assembly.
GetManifestResourceStream(BloombergSecurityDataResource), "Security.txt");
SecurityFileValidator validator = new SecurityFileValidator();
Assert.IsTrue(validator.Validate(Path.Combine(tempDir.DirectoryPath, "Security.txt"));
}
}
Here is the code... Enjoy!
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.AccessControl;
namespace PebbleSteps.TestingTools
{
/// <summary>
/// Creates a Temporary Directory that is deleted when the Dispose method is called. The class can be used
/// in Unit tests.
/// </summary>
public class TemporaryDirectory : IDisposable
{
private bool disposed = false;
private DirectoryInfo _directory;
/// <summary>
/// FullName of the folder. Files created here will be automatically deleted
/// </summary>
public string FullName {get {return _directory.FullName;}}
// The class constructor.
public TemporaryDirectory()
{
// Create the temporary directory
string directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
_directory = Directory.CreateDirectory(directoryPath);
// Change security to full control so that directory can be used by
// services under test
DirectorySecurity dirSec = _directory.GetAccessControl();
dirSec.AddAccessRule(
new FileSystemAccessRule("Everyone", FileSystemRights.FullControl,AccessControlType.Allow));
_directory.SetAccessControl(dirSec);
}
/// <summary>
/// Persists a stream to a file that is created in the temporary directory
/// </summary>
/// <param name="s"></param>
/// <param name="fileName"></param>
public void StoreToDirectory(Stream s, string fileName)
{
using (StreamReader r = new StreamReader(s))
{
using (StreamWriter w = new StreamWriter(Path.Combine(_directory.Name, Path.Combine(_directory.FullName, fileName))))
{
w.Write(r.ReadToEnd());
}
}
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
// Cleanup Managed resources
}
// Cleanup Unmanaged resources
Directory.Delete(_directory.FullName, true);
disposed = true;
}
}
~TemporaryDirectory()
{
Dispose(false);
}
#endregion
}
}