Having an actual decimal value as parameter for an attribute (example xUnit.net's [InlineData]
Asked Answered
G

6

40

I'm trying to do unit testing with xUnit.net. I want a 'Theory' test with '[InlineData]' which includes 'decimals':

[Theory]
[InlineData(37.60M)]
public void MyDecimalTest(decimal number)
{
    Assert.Equal(number, 37.60M);
}

This is not possible because you cannot create a decimal as a constant.

Question:
Is there a workaround for this?

Granth answered 16/6, 2016 at 8:49 Comment(1)
Also see Why “decimal” is not a valid attribute parameter type?Followthrough
E
45

You should be able use the String value in the Attribute and set the Parameter type to Decimal, it get's converted automatically by the Test Framework as far as I can tell.

[Theory]
[InlineData("37.60")]
public void MyDecimalTest(Decimal number)
{
    Assert.Equal(number, 37.60M);
}

If this doesn't work then you can manually convert it by passing in a String parameter.

[Theory]
[InlineData("37.60")]
public void MyDecimalTest(String number)
{
    var d = Convert.ToDecimal(number);
    Assert.Equal(d, 37.60M);
}
Eggers answered 16/6, 2016 at 9:3 Comment(6)
Nice idea. A little cleaner than casting the other value to double as well. Still hacky though.Granth
@TimPohlmann I've updated my answer based on further experiments, where the tests pass on my machineEggers
Yeah this works. I tried it before but had a different issue failing and I just assumed it had converted it to a double. My bad. Thanks!Granth
Consider Convert.ToDecimal(number, NumberFormatInfo.InvariantInfo) or equivalently Convert.ToDecimal(number, CultureInfo.InvariantCulture) if there is a possibility the current culture uses a different number format. Otherwise this may come out as 3760 (three thousand seven hundred and sixty).Followthrough
In order to first code snippet, for me it doesn't compile. The value is not convertible to the method parameter 'number' of type 'decimal'.Romanticism
@PiotrKwiatek - It should compile, but could fail to run. See Jeppe's comment. As I cannot see how xunit does the conversion I'm guessing it must be a culture related thing. You may need to use your culture's decimal point character instead of ..Eggers
C
28

Instead of InlineData, use MemberData as shown here. This gives you much greater flexibility in setting up multiple tests and allows the use of decimals or any other non-constant type.

public class CalculatorTests  
{

    public static TheoryData<decimal, decimal, decimal> Data =>
        new()
        {
            { 1.2M, 2.1M, 3.3M },
            { -4.000M, -6.123M, -10.123M }
        };

    [Theory]
    [MemberData(nameof(Data))]
    public void CanAddTheoryMemberDataProperty(decimal value1, decimal value2, decimal expected)
    {
        var calculator = new Calculator();

        var result = calculator.Add(value1, value2);

        Assert.Equal(expected, result);
    }
}
Cerumen answered 19/5, 2018 at 18:20 Comment(0)
M
3

The NUnit solution (Google landed me here) The accepted answers gave me some ideas so after some research came up with with help from NUnit Sequential Attribute with arrays in Values. The nameof expression is a c# 7 expression

[TestFixture]
public class CalculatorTests
{

    public static IEnumerable<object[]> Data =>
        new List<object[]>
        {
            new object[] { 1.2M, 2.1M, 3.3M },
            new object[] { -4.000M, -6.123M, -10.123M }
        };

    [Test]
    [TestCaseSource(nameof(Data))]
    public void CanAddTheoryMemberDataProperty(decimal value1, decimal value2, decimal expected)
    {
        var calculator = new Calculator();

        var result = calculator.Add(value1, value2);

        Assert.AreEqual(expected, result);
    }
}
Mccollough answered 21/8, 2018 at 11:40 Comment(1)
did you just copy the May 19, 2018 solution???Misfeasance
S
3

As @StillLearning already suggested you can use the MemberData attribute. Nowadays there is the TheoryData<> type to make your input typesafe.

I (currently) cannot edit that answer so I'm posting it as another answer.

public class CalculatorTests  
{

    public static TheoryData<decimal, decimal, decimal> Data =>
        new()
        {
            { 1.2M, 2.1M, 3.3M },
            { -4.000M, -6.123M, -10.123M }
        };

    [Theory]
    [MemberData(nameof(Data))]
    public void CanAddTheoryMemberDataProperty(decimal value1, decimal value2, decimal expected)
    {
        var calculator = new Calculator();

        var result = calculator.Add(value1, value2);

        Assert.Equal(expected, result);
    }
}
Stockholder answered 13/12, 2022 at 10:18 Comment(0)
B
-1

Don't rely on automatic conversion (suggested by Artem Vertiy) because it causes precision loss.

enter image description here

Instead use string to decimal conversion (suggested in the accepted answer).


enter image description here

public static class MathLib
{
    public static decimal Square(decimal x)  => x * x;
}

public class MathLibTest
{
    [Theory]
    [InlineData("2.0000000001", "4.00000000040000000001")]
    public void Correct(string xs, string cs)
    {
        var x = decimal.Parse(xs);
        var control = decimal.Parse(cs);
        var sut = MathLib.Square(x);
        Assert.Equal(control, sut);
    }

    [Theory]
    [InlineData(2.0000000001, 4.00000000040000000001)]
    public void Wrong(decimal x, decimal control)
    {
        var sut = MathLib.Square(x);
        Assert.Equal(control, sut);
    }
}
Bureaucratize answered 16/6, 2016 at 8:49 Comment(0)
C
-1

simply use double or int values they will be casted to decimal implicitly (caution double is a floating point number, possible precision issues when dealing with very large numbers, very small numbers, or when needing exact precision e.g. financial calculations)

[Theory]
[InlineData(10,15.5)]
public void Test(decimal amountA, decimal amountB)
Cordate answered 7/5 at 7:23 Comment(1)

© 2022 - 2024 — McMap. All rights reserved.