Is it possible to make xUnit test work when you don't specify optional parameter values in InlineDataAttribute
?
Example:
[Theory]
[InlineData(1, true)] // works
[InlineData(2)] // error
void Test(int num, bool fast=true){}
Yes it is. There are many ways to do it by redefining some original xunit attributes.
The following code is one of them, which would give you some idea.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class OptionalTheoryAttribute : TheoryAttribute
{
protected override IEnumerable<ITestCommand> EnumerateTestCommands(IMethodInfo method)
{
var result = (List<ITestCommand>)base.EnumerateTestCommands(method);
try
{
return TransferToSupportOptional(result, method);
}
catch (Exception ex)
{
result.Clear();
result.Add(new LambdaTestCommand(method, () =>
{
throw new InvalidOperationException(
String.Format("An exception was thrown while getting data for theory {0}.{1}:\r\n{2}",
method.TypeName, method.Name, ex)
);
}));
}
return result;
}
private static IEnumerable<ITestCommand> TransferToSupportOptional(
IEnumerable<ITestCommand> testCommands, IMethodInfo method)
{
var parameterInfos = method.MethodInfo.GetParameters();
testCommands.OfType<TheoryCommand>().ToList().ForEach(
testCommand => typeof(TheoryCommand)
.GetProperty("Parameters")
.SetValue(testCommand, GetParameterValues(testCommand, parameterInfos)));
return testCommands;
}
private static object[] GetParameterValues(TheoryCommand testCommand, ParameterInfo[] parameterInfos)
{
var specifiedValues = testCommand.Parameters;
var optionalValues = GetOptionalValues(testCommand, parameterInfos);
return specifiedValues.Concat(optionalValues).ToArray();
}
private static IEnumerable<object> GetOptionalValues(TheoryCommand command, ParameterInfo[] parameterInfos)
{
return Enumerable.Range(command.Parameters.Length, parameterInfos.Length - command.Parameters.Length)
.ToList().Select(i =>
{
EnsureIsOptional(parameterInfos[i]);
return Type.Missing;
});
}
private static void EnsureIsOptional(ParameterInfo parameterInfo)
{
if (!parameterInfo.IsOptional)
{
throw new ArgumentException(string.Format(
"The parameter '{0}' should be optional or specified from data attribute.",
parameterInfo));
}
}
}
internal class LambdaTestCommand : TestCommand
{
private readonly Assert.ThrowsDelegate lambda;
public LambdaTestCommand(IMethodInfo method, Assert.ThrowsDelegate lambda)
: base(method, null, 0)
{
this.lambda = lambda;
}
public override bool ShouldCreateInstance
{
get
{
return false;
}
}
public override MethodResult Execute(object testClass)
{
try
{
lambda();
return new PassedResult(testMethod, DisplayName);
}
catch (Exception ex)
{
return new FailedResult(testMethod, ex, DisplayName);
}
}
}
public class OptionalTheoryTest
{
[OptionalTheory]
[InlineData(1)]
[InlineData(1, true)]
public void TestMethod(int num, bool fast = true)
{
// Arrange
// Act
// Assert
Assert.Equal(1, num);
Assert.True(fast);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With