Autofixture: ๊ธฐ๋Šฅ์š”์ฒญ: IFixture์— Clone ๋ฉ”์†Œ๋“œ ์ถ”๊ฐ€

์— ๋งŒ๋“  2020๋…„ 09์›” 24์ผ  ยท  9์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: AutoFixture/AutoFixture

์†Œ๊ฐœ

IFixture ์— Clone ๋ฉ”์†Œ๋“œ ๋˜๋Š” ์กฐ๋ช…๊ธฐ์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“œ๋Š” ์œ ์‚ฌํ•œ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ™•์žฅ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public static IFixture Clone( this IFixture fixture)
{
   var cloneFixture = new Fixture();

   cloneFixture.Behaviors.Clear();
   foreach ( var behavior in fixture.Behaviors)
   {
      cloneFixture.Behaviors.Add( behavior );
   }

   cloneFixture.Customizations.Clear();
   foreach ( var customization in fixture.Customizations )
   {
      cloneFixture.Customizations.Add( customization );
   }

   cloneFixture.OmitAutoProperties = fixture.OmitAutoProperties;
   cloneFixture.RepeatCount = fixture.RepeatCount;

   cloneFixture.ResidueCollectors.Clear();
   foreach ( var residueCollector in fixture.ResidueCollectors )
   {
      cloneFixture.ResidueCollectors.Add( residueCollector );
   }

   return cloneFixture;
}

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

์„ธ๋ถ€

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

fixture.Customizations.Add( new FilteringSpecimenBuilder(
            new FixedBuilder( mediaId ),
            new ParameterSpecification( 
                typeof( ObjectId ), 
                "mediaId" ) ) );
var mediaVM = fixture.Build<MediaViewModel>()   
                     .With( mvm => mvm.ParentMixerId, mixerId )
                     .With( mvm => mvm.TrackId, trackId )
                     .Create();

_ = fixture.Customizations.Remove( fixture.Customizations.Last() );

//...

์‚ฌ์šฉ์ž ์ง€์ •์„ ์ œ๊ฑฐํ•œ ์ด์œ ๋Š” ๋งˆ์ง€๋ง‰์— ์ถ”๊ฐ€๋œ ์‚ฌ์šฉ์ž ์ง€์ •์ด ๋” ๋†’์€ ์šฐ์„  ์ˆœ์œ„๋ฅผ ๊ฐ–๊ณ  ์‚ฌ์šฉ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ์‹œ๋„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์ข…์˜ ํ™•์žฅ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๊ฒƒ์„ ๋‹จ์ˆœํ™”ํ•˜๋ ค๊ณ  ํ•˜๋ฉด:

public static IFixture AddCustomization<T>( this IFixture fixture, T value, string name )
{
   fixture.Customizations.Add( new FilteringSpecimenBuilder(
         new FixedBuilder( value ),
         new ParameterSpecification(
            typeof( T ),
            name ) ) );
   return fixture;
}

์–ด๋–ค ๊ณณ์—์„œ๋Š” ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ฐœ์ฒด๊ฐ€ ๋นŒ๋“œ๋œ ํ›„ ๊ณ ์ • ์žฅ์น˜์— ๋” ์ด์ƒ ์›ํ•˜์ง€ ์•Š๊ณ  ์ œ๊ฑฐํ•ด์•ผ ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜๊ฐ€ ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋œ ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์ค„์ด ์—†๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด ํ™•์žฅ ๋ฉ”์„œ๋“œ๊ฐ€ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ณ ์ • ์žฅ์น˜์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“  ๊ฒฝ์šฐ ๋‚ด ์ƒ์„ฑ์€ ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ณ  ์›๋ž˜ ๊ณ ์ • ์žฅ์น˜๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ฐœ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ ํ›„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ •์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋‚ด ๋ฌธ์ œ๋ฅผ ๊ณ ๋ คํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :์Šค๋งˆ์ผ๋ฆฌ:

feature request

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

@ajorians ํ™œ๋™์ด ์—†์–ด ๋ฌธ์ œ๋ฅผ ๋‹ซ์œผ
๋‚˜๋Š” ๋‹น์‹ ์˜ ๋””์ž์ธ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ ค ํ–ˆ์Šต๋‹ˆ๊นŒ?

์ƒ์„ฑ์ž๋ฅผ ๋ณด๋ฉด ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์™€ ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ž ์žฌ์ ์ธ ์†”๋ฃจ์…˜์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ์„  ์‹ ๊ทœ ๋ฐ ์ฃผ์ž… ๊ฐ€๋Šฅํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐœ๋…์„ ํ˜ผํ•ฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์†”๋ฃจ์…˜์€ ์ฃผ์ž… ๊ฐ€๋Šฅํ•œ ์ข…์†์„ฑ(์„œ๋น„์Šค ์ธํ„ฐํŽ˜์ด์Šค๋ผ๊ณ ๋„ ํ•จ)์„ ์†์„ฑ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ์†์„ฑ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Fixture ๋ฅผ ๋ณต์‚ฌํ•ด์•ผ ํ•˜๋Š” ์ฆ‰๊ฐ์ ์ธ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ณ  ์ƒ์„ฑ์ž๊ฐ€ ๋” ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ๋Š” ๊ณผ๋‹ค ์ฃผ์ž… ์ฝ”๋“œ ๋ƒ„์ƒˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋ณ„๋„์˜ ์ถ”์ƒํ™”๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๋‹น์‹ ์˜ ๋””์ž์ธ์ด SRP๋ฅผ ์กด์ค‘ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋ฉฐ ํ™•์žฅ์„ ํ†ตํ•ด ํ…Œ์ŠคํŠธ์—์„œ ํ˜„์žฌ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ์—„์ฒญ๋‚œ ์–‘์˜ ์„ค์ •์„ ์ค„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค ์ด๋ฆ„์ด ViewModel ์ด๋ฏ€๋กœ MVVM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง• ๋ชจ๋ธ์„ ๋„์ž…ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋”์šฑ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ด๋ฒคํŠธ ์ˆ˜์ง‘๊ธฐ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ MVVM ํ”„๋ ˆ์ž„์›Œํฌ์—๋Š” ์ด๋Ÿฌํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋งŽ์€ ์„ค์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”.

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

@ajorians ์ข‹์€ ํ•˜๋ฃจ! ๊ท€ํ•˜์˜ ์˜ˆ๋ฅผ ์ดํ•ดํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ณผ ๋•Œ ๊ท€ํ•˜์—๊ฒŒ ํ•„์š”ํ•œ ๊ฒƒ์ด ์ด๋ฏธ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. Fixture์˜ Build() ๋ฉ”์„œ๋“œ๋Š” ์‹ค์ œ๋กœ ๋‚ด๋ถ€ ๊ทธ๋ž˜ํ”„์˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์œ ์ง€ํ•˜์ง€ ์•Š๊ณ  AutoFixture ์œ„์— "์ผํšŒ์„ฑ" ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ ๋ฐ๋ชจ๋ฅผ ๊ฒ€ํ† ํ•˜์„ธ์š”:

```c#
๊ณต๊ฐœ ํด๋ž˜์Šค ๋ชจ๋ธ
{
๊ณต๊ฐœ ์ •์ˆ˜ ID { ๊ฐ€์ ธ์˜ค๊ธฐ; ์„ธํŠธ; }
๊ณต๊ฐœ ๋ฌธ์ž์—ด ์ด๋ฆ„ { get; ์„ธํŠธ; }
}

[์‚ฌ์‹ค]
๊ณต๊ฐœ ๋ฌดํšจ ๋ฐ๋ชจ()
{
var ๊ณ ์ • ์žฅ์น˜ = new Fixture();
์ •์ฐฉ๋ฌผ.์‚ฌ์šฉ์ž ์ •์˜(c => c.With(m => m.Id, 42));

var value1 = fixture.Create<Model>();
Assert.Equal(42, value1.Id);
Assert.NotNull(value1.Name);

var value2 = fixture.Build<Model>()
    .With(m => m.Id, (int genInt) => genInt * 2 /* make it always even */)
    .Without(x => x.Name)
    .Create();
Assert.NotEqual(42, value2.Id);
Assert.Equal(value2.Id % 2, 0);
Assert.Null(value2.Name);

var value3 = fixture.Create<Model>();
Assert.Equal(42, value3.Id);
Assert.NotNull(value3.Name);

}
```

๊ท€ํ•˜์˜ ์š”๊ตฌ ์‚ฌํ•ญ์„ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•˜์—ฌ ๋„์›€์„ ๋“œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”!

์•ˆ๋…•ํ•˜์„ธ์š” @zvirja ,

์•ˆ๋…•ํ•˜์„ธ์š”!

๋‚ด๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด; ์šฐ๋ฆฌ์˜ Model ํด๋ž˜์Šค์—๋Š” set ๊ฐ€ ์—†์œผ๋ฉฐ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๊ฒƒ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋Œ€์ฒดํ•œ๋‹ค๋ฉด:

public class Model
{
   public Model( int id, string name )
   {
      Id = id;
      Name = name;
   }

   public int Id { get; }
   public string Name { get; }
}

์›๋ž˜ ์ง€์ •ํ•˜์ง€ ์•Š์•„์„œ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ ์‚ฌ์šฉ์ž ์ •์˜๋Š” Build() ์ž…๋‹ˆ๋‹ค.

๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ์ƒ์„ฑ์ž์—์„œ ๋งŽ์€ ์ผ์„ ํ•˜๊ณ  setter ์—†์ด๋Š” readonly ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜๊ณ  ๋ฉ”์„œ๋“œ๊ฐ€ ์„ค์ •๋  ๋•Œ๊นŒ์ง€ ์„ค์ • ํ•ด์ œ๋  ์ˆ˜ ์—†๋„๋ก ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š”.

๋” ๋‚˜์€ ์˜๋ฏธ๊ฐ€ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋„์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! :์Šค๋งˆ์ผ๋ฆฌ:

@ajorians ๋งŽ์€ ์–‘์˜ ์ž…๋ ฅ ์ธ์ˆ˜๊ฐ€ ์žˆ๋Š” ๋ชจ๋ธ์ด ์žˆ์œผ๋ฏ€๋กœ ๋ช…์‹œ์ ์œผ๋กœ ๊ฑฑ์ •ํ•  ํ•„์š” ์—†์ด ๊ทธ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋ ค๋Š” ๋ฌธ์ œ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ 

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ํ•ญ์ƒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
c# var value2 = fixture.Build<Model>() .FromFactory((int id, string name) => new Model(id, name)) .Create();

๋ถˆํ–‰ํžˆ๋„, ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•จ์— ๋”ฐ๋ผ ํ™•์žฅ์„ฑ์ด ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค ๐Ÿ˜Ÿ

@ajorians ๋งŽ์€ ์–‘์˜ ์ž…๋ ฅ ์ธ์ˆ˜๊ฐ€ ์žˆ๋Š” ๋ชจ๋ธ์ด ์žˆ์œผ๋ฏ€๋กœ ๋ช…์‹œ์ ์œผ๋กœ ๊ฑฑ์ •ํ•  ํ•„์š” ์—†์ด ๊ทธ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋ ค๋Š” ๋ฌธ์ œ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ 

๋„ค, ๋งž์Šต๋‹ˆ๋‹ค.

๋ถˆํ–‰ํžˆ๋„, ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•จ์— ๋”ฐ๋ผ ํ™•์žฅ์„ฑ์ด ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค ๐Ÿ˜Ÿ

์šด ์ข‹๊ฒŒ๋„ Freeze ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š” ๋งŽ์€ ํด๋ž˜์Šค์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ˆ, ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค์—๋Š” ํ˜„์žฌ 30 ๋งค๊ฐœ๋ณ€์ˆ˜๋ณด๋‹ค ๋งŽ์€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
image

๊ทธ๋ฆฌ๊ณ  ์•„๋งˆ๋„ ์˜ฌํ•ด๊ฐ€ ๋๋‚˜๊ธฐ ์ „์— 4 ์ •๋„ ์ถ”๊ฐ€๋กœ ์„ฑ์žฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด ๋น„์ •์ƒ์  ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

์ด ๋ชจ๋“  ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ €์™€ ํ•จ๊ป˜ ๋ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :smiley:

@ajorians ํ™œ๋™์ด ์—†์–ด ๋ฌธ์ œ๋ฅผ ๋‹ซ์œผ
๋‚˜๋Š” ๋‹น์‹ ์˜ ๋””์ž์ธ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ ค ํ–ˆ์Šต๋‹ˆ๊นŒ?

์ƒ์„ฑ์ž๋ฅผ ๋ณด๋ฉด ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์™€ ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ž ์žฌ์ ์ธ ์†”๋ฃจ์…˜์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ์„  ์‹ ๊ทœ ๋ฐ ์ฃผ์ž… ๊ฐ€๋Šฅํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐœ๋…์„ ํ˜ผํ•ฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์†”๋ฃจ์…˜์€ ์ฃผ์ž… ๊ฐ€๋Šฅํ•œ ์ข…์†์„ฑ(์„œ๋น„์Šค ์ธํ„ฐํŽ˜์ด์Šค๋ผ๊ณ ๋„ ํ•จ)์„ ์†์„ฑ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ์†์„ฑ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Fixture ๋ฅผ ๋ณต์‚ฌํ•ด์•ผ ํ•˜๋Š” ์ฆ‰๊ฐ์ ์ธ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๊ณ  ์ƒ์„ฑ์ž๊ฐ€ ๋” ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ๋Š” ๊ณผ๋‹ค ์ฃผ์ž… ์ฝ”๋“œ ๋ƒ„์ƒˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋ณ„๋„์˜ ์ถ”์ƒํ™”๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๋‹น์‹ ์˜ ๋””์ž์ธ์ด SRP๋ฅผ ์กด์ค‘ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋ฉฐ ํ™•์žฅ์„ ํ†ตํ•ด ํ…Œ์ŠคํŠธ์—์„œ ํ˜„์žฌ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ์—„์ฒญ๋‚œ ์–‘์˜ ์„ค์ •์„ ์ค„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค ์ด๋ฆ„์ด ViewModel ์ด๋ฏ€๋กœ MVVM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง• ๋ชจ๋ธ์„ ๋„์ž…ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋”์šฑ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ด๋ฒคํŠธ ์ˆ˜์ง‘๊ธฐ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ MVVM ํ”„๋ ˆ์ž„์›Œํฌ์—๋Š” ์ด๋Ÿฌํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋งŽ์€ ์„ค์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”.

์•ˆ๋…•ํ•˜์„ธ์š” @aivascu ๋‹˜

@ajorians ํ™œ๋™์ด ์—†์–ด ์ด์Šˆ๋ฅผ ์ข…๋ฃŒํ•˜๋ ค ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ œ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ด ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”. ๊ตฌํ˜„๋  ๋•Œ๊นŒ์ง€ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ ์š”์ฒญ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋‹นํžˆ ๋งŽ์€ ๋น„ํ™œ์„ฑ ์ƒํƒœ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์˜ ๋””์ž์ธ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ ค ํ–ˆ์Šต๋‹ˆ๊นŒ?

ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ํ”„๋ฆฌ์ฆ˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‹น์‹ ์ด ์–ธ๊ธ‰ ํ•œ ํ•ญ๋ชฉ ์ค‘ ์ผ๋ถ€์— ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์Šฌํ”„๊ฒŒ๋„ ๋‚˜๋Š” null ํ•œ ๋ฒˆ ํ™•์ธ๋˜๊ณ  readonly ๋˜๊ณ  ํ•ญ์ƒ null ๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ๋ชจ๋“  ๊ฒƒ์„ ์ƒ์„ฑ์ž์— ๋„ฃ๋Š” ์ฒ ํ•™์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ๋ณด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค

๋„์›€์ด ๋˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”.

์ด ๋ชจ๋“  ๊ฒƒ์ด ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์ „ ์„ธ๊ณ„ ์‚ฌ๋žŒ๋“ค๋„ ์šฐ๋ฆฌ๊ฐ€ ๋ณ€ํ™”๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ํŒ€์— ์•Œ๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ IFixture ๊ฐ€ Clone ๋˜๋Š” ์ด์™€ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ธ์ง€ ๋ฌผ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์–ด๋–ป๊ฒŒ๋“  ์‚ฌ๋žŒ๋“ค์ด ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ๋งˆ์ง€๋ง‰์œผ๋กœ ์ถ”๊ฐ€๋œ ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ pop ํ•ด์ œํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „ ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ€๋„๋ก ํ—ˆ์šฉํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ด ์›ํ•˜๋Š” ๋™์ž‘์„ ์œ ๋ฐœํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋ฉฐ ์‹œ๋‚˜๋ฆฌ์˜ค์—๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ข‹์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์—†๋Š” ํ•œ ๋ณต์žก์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์— ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚ ์ง€๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด ์„ ํƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ œ๊ฐ€ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :smiley:

@ajorians ์ œ ์˜๊ฒฌ์€ .Clone() ๋ฉ”์„œ๋“œ๋ฅผ Fixture ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋ณด๊ธฐ์— ๋‹จ์ผ ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•ด ์ฃผ์–ด์ง„ ์‹œ๊ฐ„์— ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜๋งŒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณผ๊ฑฐ์— unfreeze/eject/freeze-once ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ๋‹ฌ๋ผ๋Š” ์š”์ฒญ์ด ๋ช‡ ์ฐจ๋ก€ ์žˆ์—ˆ์ง€๋งŒ ์•„๋ฌด๋„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
๊ทธ ์ด์œ ๋Š” ๋™๊ฒฐ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๋นŒ๋” ๋˜๋Š” ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฃผ์ž…๋œ ๊ฐ’์ด ๋ฌด์—‡์ธ์ง€ ์•ˆ์ •์ ์œผ๋กœ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

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

public class CountingRelay : ISpecimenBuilder
{
    public CountingRelay(ISpecimenBuilder builder, int maxCount)
    {
        this.MaxCount = maxCount;
        this.Builder = builder;
    }

    public int Count { get; private set; }
    public int MaxCount { get; }
    public ISpecimenBuilder Builder { get; }

    public object Create(object request, ISpecimenContext context)
    {
        if (this.Count == this.MaxCount) return new NoSpecimen();
        var result = this.Builder.Create(request, context);
        if (!(result is NoSpecimen)) this.Count++;
        return result;
    }
}

์•ˆ๋…•ํ•˜์„ธ์š” @aivascu ๋‹˜

@ajorians ์ œ ์˜๊ฒฌ์€ .Clone() ๋ฉ”์„œ๋“œ๋ฅผ Fixture ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ข‹์•„์š”. ์Œ, ๊ณ ๋ คํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์ด ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์€ ํŠน์ • ํšŸ์ˆ˜์˜ ์„ฑ๊ณต์ ์ธ ํ•ด๊ฒฐ ํ›„์— ์š”์ฒญ์„ ๋ฌด์‹œํ•˜๋Š” ๋ฆด๋ ˆ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ณผ๊ฒŒ์š”.

๋‹ค์‹œ ํ•œ๋ฒˆ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! :์Šค๋งˆ์ผ๋ฆฌ:

@ajorians ๋ช…์‹œ์ ์ด๊ณ  ์ž๋™ ์‚ฌ์šฉ์ž ์ง€์ • ์žฌ์„ค์ •์„ ์œ„ํ•ด ๋ณด๋‹ค ๊ณต์‹์ ์ธ ๊ธฐ๋Šฅ ์š”์ฒญ #1214๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์ด ๊ธฐ๋Šฅ์˜ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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