xUnit测试引擎的InlineDataAttribute +可选方法参数

时间:2013-02-11 18:25:45

标签: c# unit-testing optional-parameters xunit optional-arguments

如果未在InlineDataAttribute中指定可选参数值,是否可以使xUnit测试工作?

示例:

[Theory]
[InlineData(1, true)] // works
[InlineData(2)]       // error
void Test(int num, bool fast=true){}

1 个答案:

答案 0 :(得分:2)

是的。有很多方法可以通过重新定义一些原始的xunit属性来实现。

以下代码就是其中之一,可以给你一些想法。

[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);
    }
}