Nunit: Prise en charge des paramètres de type générique pour TestCaseSourceAttribute

Créé le 15 nov. 2017  ·  4Commentaires  ·  Source: nunit/nunit

Je travaille sur des cas de test qui pourraient être mieux conçus en utilisant des génériques. J'essaie d'utiliser TestCaseSource avec de nombreuses combinaisons de types différentes.

Le problème que je rencontre est l'utilisation de la réflexion dans mes tests pour invoquer une méthode de test générique que j'utilise pour la validation d'objet.

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

Cependant, je pense que la conception suivante peut ajouter de la valeur dans ce cas :

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

J'ai fait des extensions à TestCaseSourceAttribute localement pour supporter cela.

Est-ce quelque chose d'ouvert pour une contribution ? Je veux clarifier la conception avant de créer un PR.

design enhancement

Commentaire le plus utile

Cela pourrait fonctionner aussi. @nunit/framework-team, quelqu'un d'autre trouve ce sujet intéressant ?

Tous les 4 commentaires

Est-ce quelque chose d'ouvert pour une contribution ? Je veux clarifier la conception avant de créer un PR.

Merci beaucoup d'avoir entamé la conversation et proposé votre aide. Nous voulons également clarifier la conception avant de créer un PR. =D Il finira probablement par être ouvert pour une contribution bientôt !

Même demande mais pour TestCaseAttribute , pas TestCaseSourceAttribute : https://github.com/nunit/nunit/issues/1215
Inférence uniquement : https://github.com/nunit/nunit/issues/150

Je dois déjà écrire du code sous-optimal aujourd'hui, donc je le veux absolument.

Dans le constructeur TestCaseData , pensez-vous qu'il existe un moyen de différencier plus visuellement les valeurs Type qui remplissent les paramètres de méthode génériques des valeurs Type qui remplissent les paramètres de méthode ordinaires ?

Pour la découvrabilité : qu'en est-il de new TestCaseData<T1, T2>(ordinaryArg1, ordinaryArg2)? ?

Ou ceci, qui nous éviterait de déclarer N nouvelles classes TestCaseData :
TestCaseData.Create<T1, T2>(ordinaryArg1, ordinaryArg2, ordinaryArg3)

Qu'en est-il de l'utilisation d'une méthode telle que TestCaseData.GenericsArgs(params Type[] generics) similaire à la méthode Returns.

Cela pourrait fonctionner aussi. @nunit/framework-team, quelqu'un d'autre trouve ce sujet intéressant ?

Ce numéro est marqué comme une idée/conception/discussion mais n'a pas eu de contributions depuis des années donc je ferme. Si quelqu'un est intéressé à travailler sur cette idée, affichez votre intérêt et l'équipe envisagera de rouvrir.

Cette page vous a été utile?
0 / 5 - 0 notes