Ich arbeite an einigen Testfällen, die mit Generika besser gestaltet werden können. Ich versuche, TestCaseSource
mit vielen verschiedenen Kombinationen von Typen zu verwenden.
Das Problem, auf das ich stoße, ist die Verwendung von Reflektion in meinen Tests, um eine generische Testmethode aufzurufen, die ich für die Objektvalidierung verwende.
[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);
}
}
Ich glaube jedoch, dass das folgende Design in diesem Fall einen Mehrwert schaffen kann:
[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);
}
Ich habe lokal Erweiterungen für TestCaseSourceAttribute
vorgenommen, um dies zu unterstützen.
Ist das etwas für einen Beitrag offen? Ich möchte zuerst das Design klären, bevor ich eine PR erstelle.
Ist das etwas für einen Beitrag offen? Ich möchte zuerst das Design klären, bevor ich eine PR erstelle.
Vielen Dank, dass Sie das Gespräch begonnen und Ihre Hilfe angeboten haben. Wir möchten auch das Design klären, bevor Sie eine PR erstellen. =D Es wird wahrscheinlich bald für einen Beitrag offen sein!
Gleiche Frage, aber für TestCaseAttribute
, nicht TestCaseSourceAttribute
: https://github.com/nunit/nunit/issues/1215
Nur Inferenz: https://github.com/nunit/nunit/issues/150
Ich muss heute schon suboptimalen Code schreiben, also will ich das unbedingt.
Glauben Sie, dass es im TestCaseData
-Konstruktor eine Möglichkeit gibt, Type
-Werte, die generische Methodenparameter füllen, visuell besser von Type
-Werten zu unterscheiden, die gewöhnliche Methodenparameter füllen?
Für die Auffindbarkeit: was ist mit new TestCaseData<T1, T2>(ordinaryArg1, ordinaryArg2)?
Oder dies, wodurch wir vermeiden würden, N neue TestCaseData
-Klassen zu deklarieren:
TestCaseData.Create<T1, T2>(ordinaryArg1, ordinaryArg2, ordinaryArg3)
Wie wäre es mit einer Methode wie TestCaseData.GenericsArgs(params Type[] Generics) ähnlich der Returns-Methode.
Das könnte auch funktionieren. @nunit/framework-team, findet noch jemand dieses Thema interessant?
Diese Ausgabe ist als Idee/Design/Diskussion gekennzeichnet, hatte aber seit Jahren keine Beiträge mehr, also schließe ich. Wenn jemand daran interessiert ist, an dieser Idee zu arbeiten, posten Sie Ihr Interesse und das Team wird eine Wiedereröffnung in Betracht ziehen.
Hilfreichster Kommentar
Das könnte auch funktionieren. @nunit/framework-team, findet noch jemand dieses Thema interessant?