【问题标题】:How to get current parameter value from TestContext?如何从 TestContext 获取当前参数值?
【发布时间】:2017-08-03 11:10:35
【问题描述】:

我有一个单元测试:

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

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

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

我需要这个来提高测试的可读性——enumValue 会隐式影响读取 BaseClassProperty 的结果这一事实不会降低可读性,因为在我的领域中,这种依赖性对每个人来说都是显而易见的。

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

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

最后,另一条线索导致使用ITestAction,从内部我可以访问ITest 接口,它提供TestMethod 实例。它具有Arguments 属性!不幸的是 - 它是私人的。

【问题讨论】:

  • 你需要什么价值?将其作为字符串而不是实际值可以吗?
  • @ThomasLevesque 我知道它存在于测试名称中。但是,我们有时会使用多个参数,从这样的字符串中解析值会太复杂而且不是很安全。不过感谢您的建议!
  • 我认为最好的办法是在 NUnit 存储库中请求该功能。实施起来可能不太复杂。
  • 如果不说明如何用 NUnit 术语翻译 Given/When/Then 方法,我认为这是不可能回答的。您单独指出它们是您自己设计的方法。
  • @Charlie 不,它们完全不相关。由于它们引起了一些混乱,我已将它们从 sn-p 中删除。它们只是对当前 sn-p 内容的巧妙包装。

标签: c# unit-testing nunit


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2017-04-09
    • 2013-04-19
    • 1970-01-01
    • 2019-09-08
    • 2019-01-30
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多