我正在研究一些使用泛型设计得更好的测试用例。 我正在尝试将TestCaseSource
与许多不同的类型组合一起使用。
我遇到的问题是在我的测试中使用反射来调用我用于对象验证的通用测试方法。
[TestFixture]
public class SomeTests
{
public static IEnumerable<TestCaseData> TestCases()
{
yield return
new TestCaseData(
typeof(Foo),
typeof(Bar),
Generator.Foo,
Assertions.Foo);
yield return
new TestCaseData(
typeof(Bar),
typeof(Foo),
Generator.Bar,
Assertions.Bar);
}
[TestCaseSource(nameof(TestCases))]
public void CheckSomeTypes(
Type sourceType,
Type destinationType,
TSource source,
Action<object, object> assert)
{
GetType()
.GetMethod(nameof(CheckSomeTypesImpl), BindingFlags.Public | BindingFlags.Instance)
.MakeGenericType(new[] { sourceType, destinationType })
.Invoke(this, new object[] { source, assert });
}
public void CheckSomeTypesImpl<TSource, TDestination>(
TSource source,
Action<TSource, TDestination> assert)
{
var destination = Mapper.Map<TSource, TDestination>(source);
// Assert are equiviliant
assert(source, destination);
}
}
但是,我相信以下设计可能会在这种情况下增加价值:
[TestFixture]
public class SomeTests
{
public static IEnumerable<TestCaseData> TestCases()
{
yield return
new TestCaseData(
typeof(Foo),
typeof(Bar),
Generator.Foo,
Assertions.Foo);
yield return
new TestCaseData(
typeof(Bar),
typeof(Foo),
Generator.Bar,
Assertions.Bar);
}
[TestCaseSource(nameof(TestCases))]
public void CheckSomeTypes<TSource, TDestination>(
TSource source,
Action<TSource, TDestination> assert)
{
var destination = Mapper.Map<TSource, TDestination>(source);
// Assert are equiviliant
assert(source, destination);
}
}
public class Foo
{
public string Qux { get; set; }
}
public class Bar
{
public string Qux { get; set; }
}
public static class Generator
{
public static Foo Foo =>
new Foo { Qux = "TestTestTest" };
public static Bar Bar =>
new Foo { Bar = "AnotherAnotherAnother" };
}
public static class Mapper
{
// Generic mapper function
public static TDestination Map<TSource, TDestination>(TSource source)
{
if (typeof(TSource) == typeof(Foo))
{
return Map((Foo) source);
}
if (typeof(TSource) == typeof(Bar))
{
return Map((Bar) source);
}
throw new NotImplementedException($"No mapping for {typeof(TSource).FullName}.");
}
public static Foo Map(Bar bar)
{
return new Foo { Qux = bar.Qux };
}
public static Bar Map(Foo foo)
{
return new Bar { Qux = foo.Qux };
}
}
public static class Assertions
{
public static Action<Foo, Bar> Foo =>
(foo, bar) => Assert.AreEqual(foo.Qux, bar.Qux);
public static Action<Bar, Foo> Bar =>
(bar, foo) => Assert.AreEqual(bar.Qux, foo.Qux);
}
我在本地对TestCaseSourceAttribute
进行了扩展以支持这一点。
这是开放的贡献吗? 在创建 PR 之前,我想先阐明设计。
这是开放的贡献吗? 在创建 PR 之前,我想先阐明设计。
非常感谢您开始对话并提供帮助。 我们也想在您创建 PR 之前澄清设计。 =D 它可能很快就会开放供捐款!
同样的要求,但对于TestCaseAttribute
,而不是TestCaseSourceAttribute
: https ://github.com/nunit/nunit/issues/1215
仅推理: https ://github.com/nunit/nunit/issues/150
我今天已经不得不编写次优代码,所以我绝对想要这个。
在TestCaseData
构造函数中,您是否认为有一种方法可以更直观地区分填充泛型方法参数的 $ Type
值和填充普通方法参数的Type
值?
对于可发现性: new TestCaseData<T1, T2>(ordinaryArg1, ordinaryArg2)?
怎么样?
或者这样,这将允许我们避免声明 N 个新的TestCaseData
类:
TestCaseData.Create<T1, T2>(ordinaryArg1, ordinaryArg2, ordinaryArg3)
如何使用类似于 Returns 方法的 TestCaseData.GenericsArgs(params Type[] generics) 方法。
那也可以。 @nunit/framework-team,还有其他人觉得这个话题很有趣吗?
这个问题被标记为一个想法/设计/讨论,但多年来没有任何贡献,所以我要结束了。 如果有人有兴趣研究这个想法,请发表您的兴趣,团队将考虑重新开放。
最有用的评论
那也可以。 @nunit/framework-team,还有其他人觉得这个话题很有趣吗?