Nunit: Поддержка параметров универсального типа для TestCaseSourceAttribute.

Созданный на 15 нояб. 2017  ·  4Комментарии  ·  Источник: nunit/nunit

Я работаю над некоторыми тестовыми примерами, которые могут быть лучше разработаны с использованием дженериков. Я пытаюсь использовать 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.

design enhancement

Самый полезный комментарий

Это тоже может сработать. @nunit/framework-team, кому еще интересна эта тема?

Все 4 Комментарий

Это что-то открытое для вклада? Я хочу сначала уточнить дизайн, прежде чем создавать 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)

Как насчет использования такого метода, как TestCaseData.GenericsArgs(params Type[] generics), аналогичного методу Returns.

Это тоже может сработать. @nunit/framework-team, кому еще интересна эта тема?

Эта проблема помечена как идея/дизайн/обсуждение, но в ней не было комментариев в течение многих лет, поэтому я закрываю. Если кто-то заинтересован в работе над этой идеей, сообщите о своем интересе, и команда рассмотрит возможность повторного открытия.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги