Search code examples
c#testingnunitnunit-3.0

nUnit 3 C# - TestCaseSource - TestCaseData - Testing that exceptions are thrown


In scenarios where I am testing methods that receive more complex objects, I normally use something similar to this to test the method against many test cases:

[TestFixture()]
public class DataTransmitterFactoryTests
{
    [Test, TestCaseSource(typeof(DataTransmitterFactoryTestCases), "CreateDataTransmitterFactoryTestCases")]
    public void CreateTransmitterForStateTest(bool isException, Type type, IDataConfigurationData config)
    {
        var vtf = new DataTransmitterFactory();
        if (isException)
        {
            Assert.That(() => vtf.CreateTransmitterForState(config), Throws.TypeOf(type));
        }
        else
        {
            var xmitter = vtf.CreateTransmitterForState(config);
            Assert.IsInstanceOf(type, xmitter);
        }
    }
}

I use an IEnumerable to feed the test cases into my test:

public class DataTransmitterFactoryTestCases
{
    public static IEnumerable CreateDataTransmitterFactoryTestCases
    {
        get
        {
            yield return new TestCaseData(false, typeof(DataFileTransmitter),
                FuncMocks.DataConfigurationDataMock(x =>
                {
                    x.DataState.Returns(DataState.PA);
                    x.TransmissionType.Returns(DataTransmissionType.File);
                    return x;
                })).SetName("CreateDataTransmitterFactoryTest - PA - FileTransmitter");
            yield return new TestCaseData(false, typeof(DataWebServicePostTransmitter),
                FuncMocks.DataConfigurationDataMock(x =>
                {
                    x.DataState.Returns(DataState.PA);
                    x.RunMode.Returns(DataRunMode.Production);
                    x.TransmissionType.Returns(DataTransmissionType.WebServicePost);
                    return x;
                })).SetName("CreateDataTransmitterFactoryTest - PA - WebServicePost");
            yield return new TestCaseData(true, typeof(NotImplementedException),
                FuncMocks.DataConfigurationDataMock(x =>
                {
                    x.DataState.Returns(DataState.PA);
                    x.TransmissionType.Returns(DataTransmissionType.RestWebService);
                    return x;
                })).SetName("CreateDataTransmitterFactoryTest - PA - RestWebService");
            yield return new TestCaseData(true, typeof(NotImplementedException),
                FuncMocks.DataConfigurationDataMock(x =>
                {
                    x.DataState.Returns(DataState.PA);
                    x.TransmissionType.Returns(DataTransmissionType.Unknown);
                    return x;
                })).SetName("CreateDataTransmitterFactoryTest - PA - Unknown");
        }
    }
}

However, often, I would like to include a test case that throws an Exception. In nUnit 2, it appears that you could just add .Throws(typeof(ArgumentException) as an extension method, and the TestCase would look for that.

In this case, I am testing to make sure that the resulting factory object throws an Exception if it gets bad data from the factory. The resulting object only has an internal constructor.

Now, it appears that nUnit 3 wants you to use "Assert.That(Func..." to test exceptions. In this case, I would have to have a separate test data source for exceptions.

My hack is to pass bool isException, Type type.. into the method and use an if statement to decide if I am using an Assert.That() or Assert.IsInstance(). This seems pretty hacky, but I have not been able to find a better way.

Is there a better way to feed mixed test data (working / throws an exception) into a test without using the above hack? Something like:

public static IEnumerable TestCases
{
    get
    {
        yield return new TestCaseData(xyz).Returns(5);
        yield return new TestCaseData(xyz).Throws(typeof(ArgumentException);
    }
}

Solution

  • When we tossed out ExpectedExceptionAttribute in the NUnit 3 design, the Throws property of TestCaseData was collateral damage. Essentially, it allowed doing the same thing as ExpectedException in a TestCase rather than a test.

    I won't rehash the reasons why ExpectedExceptionAttribute was removed here - it's discussed at great length in other forums - but I will add that one common theme in all the discussion is that it makes better sense to separate the tests that throw an exception from those that test the happy path.