views:

119

answers:

2

I'm trying to unit test saving of a file. I have an interface that defines a document, and I pass a concrete object that implements that interface to the Save method, and it works in practice, but I'm trying to unit test it to ensure that it will always work (and I'm desperately trying to catch up on the unit tests after a period of 'crunch time').

My save method is pretty simple, it works like so:

public Boolean SaveDocument(IDocument document)
{
    BinaryFormatter bFormatter = new BinaryFormatter();
    FileStream fs = null;

    try
    {
        if (!Directory.Exists(folderName))
            Directory.CreateDirectory(folderName);

        String path = Path.Combine(Path.Combine(folderName, document.FileName), document.Extension);
        using (fs = new FileStream(path, FileMode.OpenOrCreate))
        {
            bFormatter.Serialize(fs, document);
        }
    }
    catch (IOException ioex)
    {
        LOG.Write(ioex.Message);
        return false;
    }

    return true;
}

and my test is:

[Test]
public void can_save_a_document()
{
    String testDirectory = "C:\\Test\\";
    m_DocumentHandler = new DocumentHandler(testDirectory);

    DynamicMock mock = new DynamicMock(typeof(IDocument));

    mock.ExpectAndReturn("get_FileName", "Test_File");
    mock.ExpectAndReturn("get_Extension", ".TST");

    m_DocumentHandler.SaveDocument(mock.MockInstance as IDocument);

    try
    {    
        Assert.IsTrue(Directory.Exists(testDirectory), "Directory was not created");
        String[] filesInTestDir = Directory.GetFiles(testDirectory);

        Assert.AreEqual(1, filesInTestDir.Length, "there is " + filesInTestDir.Length.ToString() + " files in the folder, instead of 1");
        Assert.AreEqual(Path.GetFileName(filesInTestDir[0]), "Test_File.TST");
    }
    finally
    {
        Directory.Delete(testDirectory);
        Assert.IsFalse(Directory.Exists(testDirectory), "folder was not cleaned up");
    }
}

I'm aware that serializing an interface preserves the concrete data, but will the mocked interface serialize?

+1  A: 

The BinaryFormatter uses the actual type of the object passed in - not the interface - when it serializes the data. So internally it will write something like Type:MyLib.Objects.MyObj,MyLib when you pass a real object and Type:Moq.ConcreteProxy,Moq etc. when you pass a mock object.

Using the BinaryFormatter for persistence is going to get you in trouble either way as you have to deal with versioning and memory layout differences between releases. You would be much better off establishing a well defined format for your document and writing the objects and fields manually.

Paul Alexander
A: 

If you need to test how serialization works with a class, then create some real classes for the test and use those. Mocking is useful for testing interactions between collaborating objects.

Steve Freeman