How to unit-test a class which needs a specific fi

2020-08-16 03:13发布

问题:

I'm currently trying to learn proper unit-test. So now I'm trying to write unit-tests for a class that should map data from an XML-File to the proper objects. Of course all functionality of the class is dependent on the existence of the corresponding XML-file. The XML-file is loaded in the constructor of the class.

I'm using C# with NUnit. So far I've got two tests:

[Test]
public void ShouldAllowInstanceToBeCreatedWhenXMLFileIsPresent()
{
    if (File.Exists(SettingsReader.XML_SETTINGS_PATH))
    {
        SettingsReader settingsReader = new SettingsReader();
        Assert.AreNotEqual(null, settingsReader);
    }
}

[Test]
[ExpectedException("Telekanzlei.Clientmanager.XMLDataLayer.XMLFileNotFoundException")]
public void ShouldThrowExceptionWhenXMLFileIsNotPresent()
{
    if (!File.Exists(SettingsReader.XML_SETTINGS_PATH))
    {
        SettingsReader settingsReader = new SettingsReader();
    }
        else
            throw new XMLFileNotFoundException();
    }

I'm not sure if checking the existence of the file in the test is a proper way to go, so any suggestions on those test are welcome too. But my question is, how to proceed with the following tests. Obviously all following tests are going to fail, if the XML-file is not present.

So do I assume that the XML-file is present, while keeping in mind, that a failing test could just mean that it's not? That wouldn't seem right to me.

Is there a general pattern, to handle a problem like this?

Thx for any help

edit: rewrote the second test, as it was failing if the file was actually present...

edit2: May it is helping to tell you, what the SettingsReader actually does. So far it looks like this:

public class SettingsReader
{
    public static readonly string XML_SETTINGS_PATH = "C:\\Telekanzlei\\Clientmanager_2.0\\Settings.xml";

    public XElement RootXElement { get; private set; }

    public SettingsReader()
    {
        if (!File.Exists(XML_SETTINGS_PATH))
            throw new XMLFileNotFoundException();
        using (var fs = File.OpenRead(XML_SETTINGS_PATH))
        {
            RootXElement = XElement.Load(fs);
        }
    }


}

I'm not sure, but I guess a StreamReader wouldn't be the way to go here, would it?

回答1:

The problem is not with your unit tests but with the design of the class. I'd suggest refactoring the class so it doesn't open the file but instead operates on a stream. Then your unit tests could simply replace a file stream for a memory stream - simples! :)

public class SettingsReader()
{
    public SettingsReader(System.IO.StreamReader reader)
    {
        // read contents of stream...
    }
}

// In production code:
new SettingsReader(new StreamReader(File.Open("settings.xml")));

// In unit test:
new SettingsReader(new StringReader("<settings>dummy settings</settings>"));

Remember, opening a file and parsing settings data are two very different concerns.



回答2:

If you must I suggest you use SetUp method to copy or verify that the file exist. I suggest making sure the file is present by adding it to the test project and marking it as "copy always" once you get that working there is no need to re-check it.
If you have a lot of tests that require external files perhaps you should use MsTest - it has an attribute called DeploymentItem that makes sure that the file is copied to the same location as the test.



回答3:

Consider rewriting code so dependencies can be passed in or somehow else stubbed for the code you want to unit-test.

I.e. pass something like "IMySettingsFileProvider" instance to SettingsReader constructor where IMySettingsFileProvider.SettingsXml returns some setting stream. This way you can mock IMySettingsFileProvider interface for the test instead of requiring file to be present on disk.



回答4:

One option is to put this at the top of the test fixture. Then the tests will only be valid when the file exists.

[SetUp]
public void Setup()
{
    Assume.That(File.Exists(SettingsReader.XML_SETTINGS_PATH));
}