Autofixture: GenerationDepthBehavior์— ๋Œ€ํ•œ ์ œ์•ˆ.

์— ๋งŒ๋“  2018๋…„ 05์›” 01์ผ  ยท  12์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: AutoFixture/AutoFixture

๋‹ค์Œ์€ ์ƒ์„ฑ ๊นŠ์ด ์ œํ•œ๊ธฐ ๋™์ž‘ ์ œ์•ˆ์ž…๋‹ˆ๋‹ค.

Fixture.Behaviors.Add(์ƒˆ๋กœ์šด GenerationDepthBehavior(2));

๋‚ด๋ถ€์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ComposeIfMultiple์„ ๋‹ค์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

```c#
๊ณต๊ฐœ ํด๋ž˜์Šค GenerationDepthBehavior : ISpecimenBuilderTransformation
{
๊ฐœ์ธ const int DefaultGenerationDepth = 1;
๊ฐœ์ธ ์ฝ๊ธฐ ์ „์šฉ int generationDepth;

public GenerationDepthBehavior() : this(DefaultGenerationDepth)
{
}

public GenerationDepthBehavior(int generationDepth)
{
    if (generationDepth < 1)
        throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");

    this.generationDepth = generationDepth;
}


public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
    if (builder == null) throw new ArgumentNullException(nameof(builder));

    return new GenerationDepthGuard(builder, new GenerationDepthHandler(), this.generationDepth);
}

}

๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค IGenerationDepthHandler
{

object HandleGenerationDepthLimitRequest(object request, IEnumerable<object> recordedRequests, int depth);

}

๊ณต๊ฐœ ํด๋ž˜์Šค DepthSeededRequest : SeededRequest
{
๊ณต๊ฐœ ์ •์ˆ˜ ๊นŠ์ด { get; }
public DepthSeededRequest(๊ฐ์ฒด ์š”์ฒญ, ๊ฐ์ฒด ์‹œ๋“œ, int ๊นŠ์ด) : base(์š”์ฒญ, ์‹œ๋“œ)
{
๊นŠ์ด = ๊นŠ์ด;
}
}

๊ณต๊ฐœ ํด๋ž˜์Šค GenerationDepthGuard : ISpecimenBuilderNode
{
๊ฐœ์ธ ์ฝ๊ธฐ ์ „์šฉ ThreadLocal> ์š”์ฒญ๋ณ„ ์Šค๋ ˆ๋“œ
= ์ƒˆ๋กœ์šด ThreadLocal>(() => ์ƒˆ ์Šคํƒ());

private Stack<DepthSeededRequest> GetMonitoredRequestsForCurrentThread() => this.requestsByThread.Value;


public GenerationDepthGuard(ISpecimenBuilder builder)
    : this(builder, EqualityComparer<object>.Default)
{
}

public GenerationDepthGuard(
    ISpecimenBuilder builder,
    IGenerationDepthHandler depthHandler)
    : this(
        builder,
        depthHandler,
        EqualityComparer<object>.Default,
        1)
{
}

public GenerationDepthGuard(
    ISpecimenBuilder builder,
    IGenerationDepthHandler depthHandler,
    int generationDepth)
    : this(
        builder,
        depthHandler,
        EqualityComparer<object>.Default,
        generationDepth)
{
}


public GenerationDepthGuard(ISpecimenBuilder builder, IEqualityComparer comparer)
{
    this.Builder = builder ?? throw new ArgumentNullException(nameof(builder));
    this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
    this.GenerationDepth = 1;
}


public GenerationDepthGuard(
    ISpecimenBuilder builder,
    IGenerationDepthHandler depthHandler,
    IEqualityComparer comparer)
    : this(
    builder,
    depthHandler,
    comparer,
    1)
{
}

public GenerationDepthGuard(
    ISpecimenBuilder builder,
    IGenerationDepthHandler depthHandler,
    IEqualityComparer comparer,
    int generationDepth)
{
    if (builder == null) throw new ArgumentNullException(nameof(builder));
    if (depthHandler == null) throw new ArgumentNullException(nameof(depthHandler));
    if (comparer == null) throw new ArgumentNullException(nameof(comparer));
    if (generationDepth < 1)
        throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");

    this.Builder = builder;
    this.GenerationDepthHandler = depthHandler;
    this.Comparer = comparer;
    this.GenerationDepth = generationDepth;
}


public ISpecimenBuilder Builder { get; }

public IGenerationDepthHandler GenerationDepthHandler { get; }

public int GenerationDepth { get; }

public int CurrentDepth { get; }

public IEqualityComparer Comparer { get; }

protected IEnumerable RecordedRequests => this.GetMonitoredRequestsForCurrentThread();

public virtual object HandleGenerationDepthLimitRequest(object request, int currentDepth)
{
    return this.GenerationDepthHandler.HandleGenerationDepthLimitRequest(
        request,
        this.GetMonitoredRequestsForCurrentThread(), currentDepth);
}

public object Create(object request, ISpecimenContext context)
{
    if (request is SeededRequest)
    {
        int currentDepth = -1;

        var requestsForCurrentThread = this.GetMonitoredRequestsForCurrentThread();

        if (requestsForCurrentThread.Count > 0)
        {
            currentDepth = requestsForCurrentThread.Max(x => x.Depth) + 1;
        }

        DepthSeededRequest depthRequest = new DepthSeededRequest(((SeededRequest)request).Request, ((SeededRequest)request).Seed, currentDepth);

        if (depthRequest.Depth >= this.GenerationDepth)
        {
            return HandleGenerationDepthLimitRequest(request, depthRequest.Depth);
        }

        requestsForCurrentThread.Push(depthRequest);
        try
        {
            return this.Builder.Create(request, context);
        }
        finally
        {
            requestsForCurrentThread.Pop();
        }
    }
    else
    {
        return this.Builder.Create(request, context);
    }
}

public virtual ISpecimenBuilderNode Compose(
    IEnumerable<ISpecimenBuilder> builders)
{
    var composedBuilder = ComposeIfMultiple(
        builders);
    return new GenerationDepthGuard(
        composedBuilder,
        this.GenerationDepthHandler,
        this.Comparer,
        this.GenerationDepth);
}

internal static ISpecimenBuilder ComposeIfMultiple(IEnumerable<ISpecimenBuilder> builders)
{
    ISpecimenBuilder singleItem = null;
    List<ISpecimenBuilder> multipleItems = null;
    bool hasItems = false;

    using (var enumerator = builders.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            singleItem = enumerator.Current;
            hasItems = true;

            while (enumerator.MoveNext())
            {
                if (multipleItems == null)
                {
                    multipleItems = new List<ISpecimenBuilder> { singleItem };
                }

                multipleItems.Add(enumerator.Current);
            }
        }
    }

    if (!hasItems)
    {
        return new CompositeSpecimenBuilder();
    }

    if (multipleItems == null)
    {
        return singleItem;
    }

    return new CompositeSpecimenBuilder(multipleItems);
}

public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
{
    yield return this.Builder;
}

IEnumerator IEnumerable.GetEnumerator()
{
    return this.GetEnumerator();
}

}

๊ณต๊ฐœ ํด๋ž˜์Šค GenerationDepthHandler : IGenerationDepthHandler
{
๊ณต๊ฐœ ๊ฐœ์ฒด HandleGenerationDepthLimitRequest(
๊ฐœ์ฒด ์š”์ฒญ,
IEnumerableRecordedRequests, int ๊นŠ์ด)
{
์ƒˆ๋กœ์šด OmitSpecimen() ๋ฐ˜ํ™˜;
}
}
```

question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@malylemire1 ์ง€๊ธˆ์€ ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์š”์ฒญ์ด ๋งŽ์„ ๋•Œ๋งŒ ๋ฏธ๋ฃจ๊ณ  ๋‚˜์ค‘์— ๊ตฌํ˜„ํ•ด๋„ ๊ดœ์ฐฎ์„๊นŒ์š”?

์›ํ•˜๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ•ต์‹ฌ์— ํฌํ•จํ•  ์ˆ˜ ์—†์–ด ์œ ๊ฐ์ž…๋‹ˆ๋‹ค ๐Ÿ˜Ÿ ์ œ๊ณต๋œ ๊ธฐ๋Šฅ๊ณผ ์—ฌ๊ธฐ์—์„œ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋“œ ์–‘ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์•ฝ๊ฐ„ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์†”๋ฃจ์…˜์—์„œ ์ด ์ž„์‹œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™์œผ๋ฏ€๋กœ ์ฐจ๋‹จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์ œ์•ˆ์— ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

๋ชจ๋“  12 ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š”, ์•„์ด๋””์–ด๋ฅผ ๊ณต์œ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! :+1:

์ด ๊ธฐ๋Šฅ์˜ ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์œ ์šฉํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ ํŠน์ • ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” ๊นŠ์ด๊ฐ€ ๋ฌด๊ธฐํ•œ ์ „ํŒŒ๋˜์ง€๋งŒ ์žฌ๊ท€์ ์ด์ง€ ์•Š์€ ORM ์—”ํ„ฐํ‹ฐ ๋˜๋Š” ์™ธ๋ถ€ API ํด๋ž˜์Šค์— ์ฃผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
์ €๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ฐ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ์— AutoFixture๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•ต์‹ฌ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ”„๋ ˆ์ž„์›Œํฌ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด ๋ฐ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๊ฐ€ ์žˆ๋Š” ๋ณต์žกํ•œ ์ž‘์—… ๋‹จ์œ„์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ. ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ๊ถŒํ•œ์ด ์žˆ๋Š” ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ.

AutoMoqDataAttribute์— ์ง์ ‘ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.

```c#
๊ณต๊ฐœ ํด๋ž˜์Šค AutoMoqDataAttribute : AutoDataAttribute
{
///


/// ๊ธฐ๋ณธ ์ƒ์„ฑ์ž.
///

/// ๋ฌด์ œํ•œ ์ƒ์„ฑ ๊นŠ์ด์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์€ 0์ž…๋‹ˆ๋‹ค.
๊ณต๊ฐœ AutoMoqDataAttribute(int ์ƒ์„ฑ ๊นŠ์ด = 0)
: ๊ธฐ๋ณธ(() =>
{
IFixture ๊ณ ์ • ์žฅ์น˜ = new Fixture().Customize(new AutoMoqCustomization());

        fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
                .ForEach(b => fixture.Behaviors.Remove(b));

        fixture.Behaviors.Add(new OmitOnRecursionBehavior());

        if (generationDepth > 0)
        {
            fixture.Behaviors.Add(new GenerationDepthBehavior(generationDepth));
        }

        return fixture;
    })
{
}

}
```

๋˜ํ•œ ComposeIfMultiple์„ ๊ณต๊ฐœํ•˜๋„๋ก ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ๋™์ž‘ ๊ตฌํ˜„์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ ํ–‰๋™์ด ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋œ ์งˆ๋ฌธ์€ ๋ชฉ์ ๊ณผ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” ๊นŠ์ด๊ฐ€ ๋ฌด๊ธฐํ•œ ์ „ํŒŒ๋˜์ง€๋งŒ ์žฌ๊ท€์ ์ด์ง€ ์•Š์€ ORM ์—”ํ„ฐํ‹ฐ ๋˜๋Š” ์™ธ๋ถ€ API ํด๋ž˜์Šค์— ์ฃผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ํ•œ ๋‹น์‹ ์ด ๋ชฉํ‘œ๋กœํ•˜๋Š” ์ฃผ์š” ๋ชฉํ‘œ๋Š” ์„ฑ๋Šฅ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์  ๊ด€์ ์—์„œ ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•˜๊ณ  CPU ํ‹ฑ ๋ช‡ ๊ฐœ๋ฅผ ์ ˆ์•ฝํ•˜๊ณ  ์‹ถ์„ ๋ฟ์ž…๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์€ ์•ฝ๊ฐ„ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ด ์‹ค์ œ๋กœ ๋งค์šฐ ๊นŠ์€ ์ค‘์ฒฉ์„ ๊ฐ€์ง„ ์œ ํ˜• ๋ชจ๋ธ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ  ๊ฑฐ๊ธฐ์—์„œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ทธ๊ฒƒ์€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์˜ ํˆฌ๋ช…์„ฑ์„ ๊นจ๋œจ๋ฆฝ๋‹ˆ๋‹ค. ์ด์ œ ํ…Œ์ŠคํŠธ ์ค‘์— ๊ฐ€์žฅ ๊นŠ์ด ์•ก์„ธ์Šคํ•œ ๊ฐœ์ฒด์˜ ์ˆ˜์ค€์„ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๊ตฌํ˜„์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ธํ•ด ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘๋‹จ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ ˆ๋ฒจ์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋ž˜ํ”„๊ฐ€ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ถ”์ ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

ํŒ€์—์„œ ์ด ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜๊ณ  ์‚ฌ์šฉ์— ๋™์˜ํ•˜๋Š” ๊ฒƒ์€ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ผ์„ ๋งŽ์ด ๋ณต์žกํ•˜๊ฒŒ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ์ž ์žฌ์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์œผ๋ฉฐ AutoFixture๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ณ ๊ธ‰ ๋น„ํŠธ๋Š” ํ™•์žฅ์„ฑ ๊ธฐ๋Šฅ์œผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์ด ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ์— ๋‚˜๋Š” ๊ทธ ๊ธฐ๋Šฅ์ด ๊ทธ๊ฒƒ์„ ์ œํ’ˆ์˜ ์ผ๋ถ€๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด์ง€ ์•Š๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์ ์€ ๊ตฌํ˜„์ด ๋งค์šฐ ์ž‘๊ธฐ ๋•Œ๋ฌธ์—(์•„๋ž˜ ์ฐธ์กฐ) ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ์—์„œ ๋กœ์ปฌ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ComposeIfMultiple์„ ๊ณต๊ฐœํ•˜๋„๋ก ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ๋™์ž‘ ๊ตฌํ˜„์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ #657์— ๋Œ€ํ•ด ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.


๊ทธ๊ฑด ๊ทธ๋ ‡๊ณ , ๊ตฌํ˜„์ด ์•ฝ๊ฐ„ ๋‹จ์ˆœํ™” ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (๋‚ด๊ฐ€ ๋ˆ„๋ฝ ๋œ ๊ฒƒ์ด ์—†๋‹ค๋ฉด).
```c#
๊ณต๊ฐœ ํด๋ž˜์Šค GenerationDepthBehavior: ISpecimenBuilderTransformation
{
๊ณต๊ฐœ ์ •์ˆ˜ ๊นŠ์ด { get; }

public GenerationDepthBehavior(int depth)
{
    Depth = depth;
}

public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
    return new RecursionGuard(builder, new OmitOnRecursionHandler(), new IsSeededRequestComparer(), Depth);
}

private class IsSeededRequestComparer : IEqualityComparer
{
    bool IEqualityComparer.Equals(object x, object y)
    {
        return x is SeededRequest && y is SeededRequest;
    }

    int IEqualityComparer.GetHashCode(object obj)
    {
        return obj is SeededRequest ? 0 : EqualityComparer<object>.Default.GetHashCode(obj);
    }
}

}
```


@moodmosaic ์˜๊ฒฌ๋„ ๊ณต์œ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๐Ÿ˜‰

https://github.com/AutoFixture/AutoFixture/issues/1032#issuecomment -385724150์— ํ‘œ์‹œ๋œ ์˜ˆ๋Š” ์ด ์ž„์‹œ ๋™์ž‘์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ ๋‚ด์žฅ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณ€๊ฒฝํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.


๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ ๊ฐ€ ๊ณ ๋ คํ• 

  • ๋ถ„ํ•  ๊ฐ€๋Šฅํ•œ PRNG ์‚ฌ์šฉ(ํ˜„์žฌ๋Š” ๋ถ„ํ•  ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์Œ)
  • ์ž„์˜์„ฑ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€(์˜ˆ: https://github.com/hedgehogqa/haskell-hedgehog/issues/125 ์ฐธ์กฐ)
  • ๋ฒค์น˜๋งˆํฌ

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด์™€ ๊ฐ™์€ ์ถ”๊ฐ€ ์ตœ์ ํ™”๋ฅผ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@moodmosaic์ด ์ œ์•ˆ์ด ๊ธฐ๋ณธ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผํ•˜๊ณ  ์‹ถ์ง€ GenerationDepthBehavior (๋‹น์‹ ์€ ํ•„์š”์— ๋”ฐ๋ผ ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋„๋ก)๋ฅผ AutoFixture ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ผ๋ถ€๋ฅผ. ์ด์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์‹ญ๋‹ˆ๊นŒ? :๋ˆˆ์ง“:

@zvirja ์‹ค์ œ๋กœ ์„ฑ๋Šฅ์ƒ์˜ ์ด์œ ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋ฌด๊ธฐํ•œ ์ „ํŒŒ์˜ ๋ฌธ์ œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์˜ˆ์™ธ๊ฐ€ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์‹คํŒจ์ž…๋‹ˆ๋‹ค.

@zvirja , @malylemire1 , ์ด ์ž„์‹œ ๋™์ž‘์ด ๊ด‘๋ฒ”์œ„ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ์•Š๋Š” ํ•œ ๊ธฐ๋ณธ ์ œ๊ณต๋  ํ•„์š”๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.


MCVE๋ฅผ ์ฐพ์„ ์ˆ˜ ๋Š” ์—†์ง€๋งŒ AFAICT, ๋ฌธ์ œ๋Š” ์‹ค์ œ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(์˜ˆ: ๊ฒฝ๊ณ„/ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ)๊ฐ€ ์•„๋‹Œ ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

AutoFixture๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค๋ฃจ๋„๋ก ์„ค๊ณ„๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์ผ๋ถ€ ์ž„์‹œ ๋™์ž‘(์—ฌ๊ธฐ์— ํ‘œ์‹œ๋œ ๊ฒƒ๊ณผ ๊ฐ™์€) ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค. +1: ๊ทธ๋ž˜๋„ ํ•ต์‹ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ผ๋ถ€์ผ ํ•„์š”๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@malylemire1 ์ง€๊ธˆ์€ ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์š”์ฒญ์ด ๋งŽ์„ ๋•Œ๋งŒ ๋ฏธ๋ฃจ๊ณ  ๋‚˜์ค‘์— ๊ตฌํ˜„ํ•ด๋„ ๊ดœ์ฐฎ์„๊นŒ์š”?

์›ํ•˜๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ•ต์‹ฌ์— ํฌํ•จํ•  ์ˆ˜ ์—†์–ด ์œ ๊ฐ์ž…๋‹ˆ๋‹ค ๐Ÿ˜Ÿ ์ œ๊ณต๋œ ๊ธฐ๋Šฅ๊ณผ ์—ฌ๊ธฐ์—์„œ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋“œ ์–‘ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์•ฝ๊ฐ„ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์†”๋ฃจ์…˜์—์„œ ์ด ์ž„์‹œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™์œผ๋ฏ€๋กœ ์ฐจ๋‹จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์ œ์•ˆ์— ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

์ •๋ฆฌ์˜ ์ผ๋ถ€๋กœ ์ด ๋ฌธ์ œ๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ์•„์ด๋””์–ด๋ฅผ ๊ณต์œ ํ•ด ์ฃผ์…”์„œ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ๐Ÿ˜‰

@malylemire1 ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ 100% ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์ด ๋„ˆ๊ฒŸ ํŒจํ‚ค์ง€๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

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