Nunit: TestCaseSourceAttribute์— ๋Œ€ํ•œ ์ผ๋ฐ˜ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜ ์ง€์›

์— ๋งŒ๋“  2017๋…„ 11์›” 15์ผ  ยท  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)

Return ๋ฉ”์†Œ๋“œ์™€ ์œ ์‚ฌํ•œ TestCaseData.GenericsArgs(params Type[] generics)์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

๊ทธ๊ฒƒ๋„ ํšจ๊ณผ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @nunit/framework-team, ์ด ์ฃผ์ œ๊ฐ€ ํฅ๋ฏธ๋กญ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์ด ๋ฌธ์ œ๋Š” ์•„์ด๋””์–ด/๋””์ž์ธ/ํ† ๋ก ์œผ๋กœ ํ‘œ์‹œ๋˜์ง€๋งŒ ๋ช‡ ๋…„ ๋™์•ˆ ๊ธฐ์—ฌํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋‹ซ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ์ด ์•„์ด๋””์–ด๋ฅผ ์ž‘์—…ํ•˜๋Š” ๋ฐ ๊ด€์‹ฌ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ด€์‹ฌ์„ ๊ฒŒ์‹œํ•˜๋ฉด ํŒ€์—์„œ ์žฌ๊ฐœ๋ฅผ ๊ณ ๋ คํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰