Nunit: Mendukung parameter tipe generik untuk TestCaseSourceAttribute

Dibuat pada 15 Nov 2017  ·  4Komentar  ·  Sumber: nunit/nunit

Saya sedang mengerjakan beberapa kasus uji yang mungkin dirancang lebih baik menggunakan obat generik. Saya mencoba menggunakan TestCaseSource dengan banyak kombinasi tipe yang berbeda.

Masalah yang saya hadapi adalah penggunaan refleksi dalam pengujian saya untuk memanggil metode pengujian umum yang saya gunakan untuk validasi objek.

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

Namun, saya yakin desain berikut dapat menambah nilai dalam hal ini:

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

Saya telah membuat ekstensi ke TestCaseSourceAttribute secara lokal untuk mendukung ini.

Apakah ini sesuatu yang terbuka untuk kontribusi? Saya ingin mengklarifikasi desain terlebih dahulu sebelum saya membuat PR.

design enhancement

Komentar yang paling membantu

Itu bisa berhasil juga. @nunit/framework-team, ada yang menganggap topik ini menarik?

Semua 4 komentar

Apakah ini sesuatu yang terbuka untuk kontribusi? Saya ingin mengklarifikasi desain terlebih dahulu sebelum saya membuat PR.

Terima kasih banyak telah memulai percakapan dan menawarkan bantuan. Kami ingin memperjelas desain sebelum Anda membuat PR juga. =D Kemungkinan akan segera terbuka untuk kontribusi!

Permintaan yang sama tetapi untuk TestCaseAttribute , bukan TestCaseSourceAttribute : https://github.com/nunit/nunit/issues/1215
Hanya kesimpulan: https://github.com/nunit/nunit/issues/150

Saya sudah harus menulis kode suboptimal hari ini, jadi saya benar-benar menginginkan ini.

Dalam konstruktor TestCaseData , apakah menurut Anda ada cara untuk membedakan nilai Type secara lebih visual yang mengisi parameter metode generik dari nilai Type yang mengisi parameter metode biasa?

Untuk dapat ditemukan: bagaimana dengan new TestCaseData<T1, T2>(ordinaryArg1, ordinaryArg2)?

Atau ini, yang memungkinkan kita untuk menghindari mendeklarasikan N kelas TestCaseData baru:
TestCaseData.Create<T1, T2>(ordinaryArg1, ordinaryArg2, ordinaryArg3)

Bagaimana dengan menggunakan metode seperti TestCaseData.GenericsArgs(params Type[] generics) yang mirip dengan metode Pengembalian.

Itu bisa berhasil juga. @nunit/framework-team, ada yang menganggap topik ini menarik?

Masalah ini ditandai sebagai ide/desain/diskusi tetapi tidak memiliki kontribusi selama bertahun-tahun, jadi saya tutup. Jika ada yang tertarik untuk mengerjakan ide ini, kirimkan minat Anda dan tim akan mempertimbangkan untuk membuka kembali.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat