Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NUNIT 3 and ExpectedException

Tags:

c#

nunit

I want to upgrade from NUNIT 2.x to 3.x but i have tests like

[TestCase("12345", ExpectedResult = "SUCCESS")]
[TestCase("invalidkey", ExpectedException = typeof(ArgumentException))]
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
public string ReturnsStatus(string filePath)
{
    // Arrange

    // Act
    Tuple<string, string> result = service.Create(filePath);

    // Assert
    return result.Item1;
}

How to rewrite this kind of tests? NUNIT 3.x does not have ExpectedException, that is my refactorization reason. I dont want to split into 3 tests. Thanks.

like image 740
Jake Manet Avatar asked Feb 16 '18 11:02

Jake Manet


3 Answers

This is an old question, but any way...

As it was mentioned, ExpectedException is considered as a bad practice, since you can't be sure what exact part of your test throws exception (in your code it can be Create(), or get_Item1()).

However, you don't need to write separate test-method for every single case. In most situations (and in your example) it's enougth to split for two tests: positive and negative. Positive case can remain unchanged. Negative case can use type of expected exception as parameter, so that:

[TestCase("",  typeof(ArgumentException))]
[TestCase(null, typeof(ArgumentNullException))]
public void ReturnStatusInvalidArgument(string filePath, Type expectedException)
{
    Assert.Throws(expectedException, () => Method(filePath));
}

Or if you prefer "ExpectedResult" style, then you can use following

    [TestCase("", ExpectedResult = typeof(ArgumentException))]
    [TestCase(null, ExpectedResult = typeof(ArgumentNullException))]
    public Type ReturnStatusInvalidArgument(string filePath)
    {
        return Assert.Catch(() => Method(filePath)).GetType();
    }
like image 162
Pavlo K Avatar answered Nov 19 '22 14:11

Pavlo K


As you discovered, NUnit 3 removed ExpectedExceptionAttribute and the related properties on TestCaseAttribute and TestCaseData.

This was done after a lot of community discussion, with less than 100% agreement but with most people recognizing that broad detection of exceptions at the level of the entire test constitutes an anti-pattern. Your example actually represents the one case where it isn't such a bad practice: a test method with only one statement. Unfortunately, you are affected by the change as well.

Two recommendations:

  1. Separate tests for "happy path" and errors.
  2. Use Assert.Throws or Assert.That(..., Throws...) for the error test.
like image 41
Charlie Avatar answered Nov 19 '22 15:11

Charlie


I think "one method==one test" is best practise:

[TestCase("12345", ExpectedResult = "SUCCESS")]
public string ReturnStatusTest_SUCCESS()
{
    return ReturnsStatus("12345");
}
[TestCase("invalidkey", ExpectedException = typeof(ArgumentException))]
public string ReturnStatusTest_SUCCESS()
{
    return ReturnsStatus("invalidkey");
}
[TestCase(null, ExpectedException = typeof(ArgumentNullException))]
public string ReturnStatusTest_SUCCESS()
{
    return ReturnsStatus(null);
}

public string ReturnsStatus(string filePath)
{
    // Arrange

    // Act
    Tuple<string, string> result = service.Create(filePath);

    // Assert
    return result.Item1;
}
like image 2
kara Avatar answered Nov 19 '22 15:11

kara