C# 如何从TestContext获取当前参数值?
我有一个单元测试:C# 如何从TestContext获取当前参数值?,c#,unit-testing,nunit,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(
[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();
}
}
如何在运行时在BaseClassProperty
中获取enumValue
参数的当前值,而不将其更改为函数并将enumValue
作为参数传递
我知道我可以走到stackframe并从那里阅读它(不,实际上,我不能:),但也许有一个更优雅的解决方案
我需要这一点来提高测试的可读性,enumValue
会隐式地影响读取BaseClassProperty
的结果,这一事实不会降低可读性,因为在我的领域中,这种依赖性对每个人来说都是显而易见的
我一直在研究TextContext
类,但它在这个上下文中似乎没有什么用处。正如在注释中提到的,该值存在于测试的名称中——但是这里不接受从字符串解析该值,因为有时我们使用多个参数和通用测试用例
另一个线索导致使用IApplyToTest
属性。但是,这需要将此属性应用于每个测试
最后,另一个线索导致使用
ITestAction
,从内部我可以访问ITest
接口,该接口提供了一个TestMethod
实例。它具有参数
属性!不幸的是,它是私有的。我已经设法创建了以下解决方法:
[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();
}
}
[AttributeUsage(AttributeTargets.Class)]
公共类TrackEnumValueActionAttribute:属性,iTestation
{
公共行动目标
{
得到
{
返回操作目标。测试;
}
}
公共空隙后测试(ITest测试)
{
}
测试前公共无效(ITest测试)
{
如果(!(test.Fixture为ICurrentExampleEnumValueHolder))
{
返回;
}
变量参数=GetArgumentsFromTestMethodorFull(测试);
if(参数==null)
{
返回;
}
var eumValue=GetEnumValueFromArgumentsOrNull(参数);
(test.Fixture作为ICurrentExampleEnumValueHolder)。CurrentEnumValue=enumValue;
}
私有对象[]GetArgumentsFromTestMethodOrNull(ITest测试)
{
回归试验
.GetType()
.GetProperty(“参数”,System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
?作为对象的GetValue(测试)[];
}
私有T?GetEnumValueFromArgumentsOrNull(对象[]参数),其中T:struct
{
返回参数
其中(a=>a.GetType().Equals(typeof(T)))
.选择(a=>(T?)a)
.FirstOrDefault();
}
}
将此属性应用于fixture的基类,并实现ICurrenteExampleEnumValueHolder
接口就可以做到这一点——我的属性(存在于接口中)的值具有一个取决于测试方法调用参数的值
不幸的是,NUnit中的
TestMethod
类的Arguments
属性是内部的,因此存在丑陋的反射解决方案。我发布了一个关于这件事的问题:你需要这个值做什么?将其作为字符串而不是实际值是否可以?@thomasleveque我知道它存在于测试名称中。但是,我们有时会使用多个参数,解析这样的字符串中的值太复杂,也不太安全。谢谢你的建议!我认为你最好的选择是在NUnit repo中请求该功能。实现起来可能不太复杂。我认为如果不说明如何用NUnit术语翻译给定的/When/Then方法,就不可能回答这个问题。您单独指出它们是您自己设计的方法。@Charlie不,它们完全不相关。由于它们引起了一些混乱,我已经从代码片段中删除了它们。它们只是对当前代码片段的巧妙包装。
[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();
}
}