如何从TestContext获取当前参数值?

时间:2017-03-13 12:23:28

标签: c# unit-testing nunit

我有一个单元测试:

[Test]
[TestCaseSource(typeof(AllExampleEnumValues))]
public void GivenSth_WhenSth_ThenSth(ExampleEnum enumValue)
{
    // Given
    var givenState = BaseClassProperty; // property from fixture's base class
    systemUnderTest.SetSomeState(givenState);

    // When
    var result = systemUnderTest.AnswerAQuestion(
        "What's the answer to"
        + " the Ultimate Question of Life,"
        + " the Universe,"
        + " and Everything?");

    // Then
    Assert.That(result, Is.EqualTo(42));
}

其中AllExampleEnumValues如下:

public class AllGranularities : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        return Enum
            .GetValues(typeof(ExampleEnum))
            .Cast<ExampleEnum>()
            .Select(ee => new object[] { ee })
            .GetEnumerator();
    }
}

在基类中我有:

protected int BaseClassProperty
{
    get
    {
        return //depending on enumValue
    }
}

如何在运行期间enumValue内获取BaseClassProperty参数的当前值,而不将其更改为函数并将enumValue作为参数传递?

我知道我可以以某种方式走到堆栈框架并从那里读取它(不,实际上,我不能:How can I get the values of the parameters of a calling method?),但也许有更优雅的解决方案?

我需要这个来改进测试&#39;可读性 - enumValue影响阅读结果的事实BaseClassProperty隐含地不会降低可读性,因为在我的域中,这种依赖性对每个人都是显而易见的。

我一直在研究TextContext类,但似乎在这种情况下没有任何用处。正如评论中所提到的,值存在于测试的名称中 - 但是这里不能接受从字符串中解析值,因为有时我们使用多个参数和通用测试用例。

另一条线索导致使用IApplyToTest属性。但是,这需要将此属性应用于每个测试。

最后,另一条路径导致使用ITestAction,我可以访问ITest接口,该接口提供TestMethod个实例。它有Arguments属性!不幸的是 - 它是私人的。

1 个答案:

答案 0 :(得分:0)

我设法创建了以下解决方法:

[AttributeUsage(AttributeTargets.Class)]
public class TrackEnumValueActionAttribute : Attribute, ITestAction
{
    public ActionTargets Targets
    {
        get
        {
            return ActionTargets.Test;
        }
    }

    public void AfterTest(ITest test)
    {
    }

    public void BeforeTest(ITest test)
    {
        if (!(test.Fixture is ICurrentExampleEnumValueHolder))
        {
            return;
        }

        var arguments = GetArgumentsFromTestMethodOrNull(test);

        if (arguments == null)
        {
            return;
        }

        var eumValue = GetEnumValueFromArgumentsOrNull<ExampleEnum>(arguments);

        (test.Fixture as ICurrentExampleEnumValueHolder).CurrentEnumValue = enumValue;
    }

    private object[] GetArgumentsFromTestMethodOrNull(ITest test)
    {
        return test
            .GetType()
            .GetProperty("Arguments", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
            ?.GetValue(test) as object[];
    }

    private T? GetEnumValueFromArgumentsOrNull<T>(object[] arguments) where T : struct
    {
        return arguments
            .Where(a => a.GetType().Equals(typeof(T)))
            .Select(a => (T?)a)
            .FirstOrDefault();
    }
}

将此属性应用于我的fixture的基类,并实现ICurrentExampleEnumValueHolder接口可以解决问题 - 我的属性(存在于接口中)的值具有取决于测试方法调用参数的值。 / p>

不幸的是,NUnit中Arguments类的TestMethod属性是内部的,因此丑陋的反射解决方法。我已就此问题发布了一个问题:https://github.com/nunit/nunit/issues/2079