Search code examples
c#nunit

Nunit testing exceptions - how to test `ArgumentOutOfRangeException(paramName, message)`


There are quite a few threads about Nunit and testing exceptions. However, I was not able to figure out how to apply this on my specific code. The parameter are causing me some trouble.

The exception I am throwing looks like this:
throw new ArgumentOutOfRangeException(paramName: nameof(valueMayNotBeZero), message: "The argument must have a positive value!");

Mentoring and comments are much appreciated.

My Question

  • What other ways are there to test the exception?
  • What best practices have you from your experience?

In this example I am creating an exception object and checking the property Message against my actual string. This seems to be a bit awkward. But this is the only way I got my test running.
var result = Assert.Throws<ArgumentOutOfRangeException>(() => testObject.ThrowAnException(valueMayNotBeZero));

Assert.That(result.Message, Is.EqualTo("The argument must have a positive value! (Parameter 'valueMayNotBeZero')"));

The Code

Program.cs

using StackOverflow;

GenerateException demo = new();
try
{
    Console.WriteLine($"Value: {demo.ThrowAnException(1.5):f2}");

}
catch (Exception e)
{
    Console.WriteLine("Unexpected Exception!");
    Console.WriteLine(e);
}

Console.WriteLine($"\n***** Press ENTER To Continue *****");
Console.ReadLine();

GenerateException.cs

namespace StackOverflow;

public class GenerateException
{
    public double ThrowAnException(double valueMayNotBeZero)
    {
        if (valueMayNotBeZero <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(valueMayNotBeZero), "The argument must have a positive value!");
        }

        return valueMayNotBeZero;
    }
}

The Test

GenerateExceptionTests.cs

using StackOverflow;

namespace SackOverflowTest;

public class GenerateExceptionTests
{
    private static IEnumerable<double> SourceProvider()
    {
        yield return 10.0;
        yield return 99.99;
    }

    private static IEnumerable<double> SourceProviderException()
    {
        // GrossSalary
        yield return 0.0;
        yield return -10.0;
    }

    [TestCaseSource(nameof(SourceProvider))]
    public void CreateNoExceptionTest(double valueMayNotBeZero)
    {
        // ***** Arrange *****
        GenerateException testObject = new();

        // ***** Act *****
        var result = testObject.ThrowAnException(valueMayNotBeZero);

        // ***** Assert *****
        Assert.That(result, Is.EqualTo(valueMayNotBeZero));
    }

    [TestCaseSource(nameof(SourceProviderException))]
    public void CreateAnExceptionTest(double valueMayNotBeZero)
    {
        // ***** Arrange *****
        GenerateException testObject = new();

        // ***** Act *****
        var result = Assert.Throws<ArgumentOutOfRangeException>(() => testObject.ThrowAnException(valueMayNotBeZero));

        // ***** Assert *****
        Assert.That(result.Message, Is.EqualTo("The argument must have a positive value! (Parameter 'valueMayNotBeZero')"));
        // somehow not working; logic error?
        //Assert.Throws<ArgumentOutOfRangeException>(() => BusinessMiniJob.CalculateEmployeeSalary(valueMayNotBeZero), "The argument must have a positive value!");
        //Assert.That(() => Throws.TypeOf<ArgumentOutOfRangeException>().And.Property(nameof(valueMayNotBeZero)).And.Message.Equals("The argument must have a positive value!"));
    }
}

Code in my current project

Why do I need to test the exception?
In my current project, I have methods receiving a double as parameter. If this value is <= 0 then the exception is thrown. And since I am at the beginning to work with unit tests, I thought this situation must be tested, too.

    public static double CalculateEmployerStatutoryPensionContribution(double grossSalary)
    {
        if (grossSalary <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(grossSalary), "The argument must have a positive value!");
        }

        return grossSalary * RentenversichungArbeitgeberPflichtanteil;
    }

Solution

  • i try to response at the last part of your question, taking into account your last example. First of all, you have to test your logic not the .NET ArgumentOutOfRangeException. For do this you should create three different scenario for testing all cases in which the value of grossSalary can be found, like that:

        public class PensionContributionTests
        {
            [Test]
            public void CalculateEmployerStatutoryPensionContribution_NegativeValue_ThrowsException()
            {
                // Arrange
                double negativeGrossSalary = -1000.0; // Adjust the value as needed
    
                // Act & Assert
                Assert.Throws<ArgumentOutOfRangeException>(() =>
                {
                    CalculateEmployerStatutoryPensionContribution(negativeGrossSalary);
                }, "The argument must have a positive value!");
            }
    
            [Test]
            public void CalculateEmployerStatutoryPensionContribution_ZeroValue_ThrowsException()
            {
                // Arrange
                double zeroGrossSalary = 0.0; // Adjust the value as needed
    
                // Act & Assert
                Assert.Throws<ArgumentOutOfRangeException>(() =>
                {
                    CalculateEmployerStatutoryPensionContribution(zeroGrossSalary);
                }, "The argument must have a positive value!");
            }
    
            [Test]
            public void CalculateEmployerStatutoryPensionContribution_PositiveValue_DoesNotThrowException()
            {
                // Arrange
                double positiveGrossSalary = 5000.0; // Adjust the value as needed
    
                // Act & Assert
                Assert.DoesNotThrow(() =>
                {
                    var result = CalculateEmployerStatutoryPensionContribution(positiveGrossSalary);
                    // You can also add assertions to check the result if needed
                });
            }
        }