Reputation: 34325
One of my unit tests has this signature:
public void FooWithFilter(string fooId, decimal? amount)
When I test it with null, it works:
[InlineData("123", null)]
But if I use an actual value, such as:
[InlineData("123", 610)]
I get an error:
System.ArgumentException Object of type 'System.Int32' cannot be
converted to type 'System.Nullable`1[System.Decimal]'.
I tried using 610M
as the attribute value, but that's not allowed as an attribute value:
An attribute argument must be a constant expression, type of expression
or array creation expression of an attribute parameter type.
Is there a way to use a nullable decimal here?
Upvotes: 21
Views: 13992
Reputation: 729
I'm using xunit 2.6.4
and I'm still getting that same error.
I tried some suggestions from other answers from this question without success.
I avoided using the MemberDataAttribute
doing this workaround:
[Theory]
[InlineData(null)]
[InlineData(42)]
public void MyTest(object testValueObj)
{
decimal? testValue = testValueObj is null ? null : Convert.ToDecimal(testValueObj);
...
}
Upvotes: 0
Reputation: 19877
Had this unit-test:
[Theory]
[InlineData(null)]
[InlineData(42)]
public void MyTest(long? testValue)
{
...
}
And when upgrading from xUnit 2.4.0 then it failed with:
System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Int64]'
The solution was to explicit specify long
-value by adding suffix L
:
[Theory]
[InlineData(null)]
[InlineData(42L)]
public void MyTest(long? testValue)
{
...
}
For decimal values then one just need to add suffix m
. Ex. 42m
.
Alternative then I could have used IConvertible testValue
and called testValue?.ToInt64(null)
or testValue?.ToDecimal(null)
.
Upvotes: 0
Reputation: 3701
There's also MemberData
if you don't want to create a new class.
For eg:
[Theory]
[MemberData(nameof(DoSomethingTestData))]
public void Should_Do_Something(string someStr, decimal? someDecimal, decimal expectedDecimal)
{
// Arrange
// Act
// Assert
}
public static IEnumerable<object[]> DoSomethingTestData()
{
var testData = new List<object[]>
{
new object[] { "SomeString", 1M, 2M },
new object[] { "SomeOtherString", null, 1M } // etc.
};
return testData;
}
Upvotes: 3
Reputation: 101748
As indicated in the comments, you can't use a decimal
here because decimal
is not one of the types that's allowed in attribute parameter values.
However, xUnit provides a more flexible way to pass parameter values to test methods, using ClassData
:
[Theory]
[ClassData(typeof(FooDataGenerator))]
public void FooWithFilter(string fooId, decimal? amount)
To use this, you simply need to define a class that extends IEnumerable<object[]>
and produces the input values you want:
public class FooDataGenerator : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] {"123", null},
new object[] {"123", 610M}
};
public IEnumerator<object[]> GetEnumerator() => _data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Some further references on the various ways of passing values to xUnit tests:
Creating Parameterised tests in xUnit
xUnit Theory: Working With InlineData, MemberData, ClassData
Upvotes: 19
Reputation: 4668
In addition to JLRishe's answer, I would suggest to inherit from TheoryData
or its generic counterparty TheoryData<>
so we get strongly typed test data and cleaner code. I find the documentation somehow thin in that respect.
[Theory]
[ClassData(typeof(FooDataGenerator))]
public void FooWithFilter(string fooId, decimal? expected)
{
// Arrange // Act // Assert
}
public class FooDataGenerator : TheoryData<string, decimal?>
{
public FooDataGenerator()
{
this.Add("987", null);
this.Add("123", 610m);
// ...
}
}
Upvotes: 11
Reputation: 833
A simple work arround for this problem is set the parameters double? and cast them to decimal? like so
[Theory]
[InlineData(1200, 1150, 50, 100)]
[InlineData(1200.50, 1200.50, 0, 0)]
[InlineData(1250.50, 1150, 0, 100.50)]
[InlineData(1150, 1150, null, null)]
[InlineData(0, null, null, null)]
[InlineData(-50, null, 50, null)]
[InlineData(50, null, null, 50)]
public void SubTotal(double valorEsperado, double? valorTotal, double? valorFrete, double? valorDesconto) =>
Assert.Equal((decimal)valorEsperado, PedidoBuilderTest.New
.AtribuirValores((decimal?)valorTotal, (decimal?)valorFrete, (decimal?)valorDesconto)
.Build().SubTotal);
Upvotes: 10