2013-07-25 4 views
6

목록의 항목이 정렬되었는지 확인하기 위해 결정 성 테스트를 어떻게 설정할 수 있습니까? 목록의 항목 단위 테스트 순서

public void SyncListContainsSortedItems(
    [Frozen] SyncItem[] expected, 
    SyncItemList sut) 
{ 
    Assert.Equal(expected.OrderBy(x => x.Key).First(), sut.First()); 
} 

그러나 모든 좋은 테스트와 같은

, 내가 먼저 내 코드를 수정하기 전에 실패를 찾았다 :

는 우선 다음과 같은했다. 물론, 운이 성공했을 때 성공했고, 후에 실패했습니다. 따라서 초기 실패는 결정적이지 않습니다.

public void SyncListContainsSortedItems(
    [Frozen] SyncItem[] seed, 
    SyncItemList sut) 
{ 
    var expected = seed.OrderByDescending(x => x.Key); 
    Assert.Equal(expected.OrderBy(x => x.Key).First(), sut.First()); 
} 

놀랍게도 그것은 또한 결정적 오류를 제공하지 않았다

두 번째로 나는 다음과 같은 생각, '반드시이 실패를 보장합니다'를했다. 냉동 씨앗이 자연히 내림차순으로 만들어지기 시작했기 때문에 깨달았습니다. 그래서 나는 상황을 개선하지 못했습니다.

지금 구현에서는 생성자를 통과하는 항목을 정렬하지 않습니다. 테스트를위한 탄탄한 기준을 설정하려면 어떻게해야합니까?

추가 정보 동기화 항목 목록 코드는 아래와 같습니다. 그 많은하지 그것이 내가 탐구하고있는 디자인과 같이

public class SyncItemList : List<SyncItem> 
{ 
    public SyncItemList(SyncItem[] input) 
    { 
     foreach (var item in input) { this.Add(item); } 
    } 
} 

업데이트 내가 테스트를 개발했습니다가. 다음 으로 작동하지만 매우 자세한 표시가 있습니다. 그것을 통과하기 위해 내 구현을 변경할

public void SyncListContainsSortedItems(IFixture fixture, List<SyncItem> seed) 
{ 
    var seconditem = seed.OrderBy(x => x.Key).Skip(1).First(); 
    seed.Remove(seconditem); 
    seed.Insert(0, seconditem); 
    var seedArray = seed.ToArray(); 

    var ascending = seedArray.OrderBy(x => x.Key).ToArray(); 
    var descending = seedArray.OrderByDescending(x => x.Key).ToArray(); 
    Assert.NotEqual(ascending, seedArray); 
    Assert.NotEqual(descending, seedArray); 

    fixture.Inject<SyncItem[]>(seedArray); 
    var sut = fixture.Create<SyncItemList>(); 

    var expected = ascending; 
    var actual = sut.ToArray(); 
    Assert.Equal(expected, actual); 
} 

한 가지 간단한 방법은 SortedSet<SyncItem> 대신 List<SyncItem>에서 상속하는 것입니다.

+0

'SyncItemList'는 어떻게 보이나요? –

+0

당신은 정말 멋지 겠지만, 속성과 UnOrdered 요청 유형이 있다면, 유한 계열과 비슷합니다. 3 개 이상의 고유 항목 만 있으면되고 순서가 잘못된 순서를 보장 할 수 있습니다. – cocogorilla

+0

어떤 버전의 AutoFixture를 사용하고 있습니까? AutoFixture 3에서 [Numbers are random] (https://github.com/AutoFixture/AutoFixture/wiki/AutoFixture-3.0-Release-Notes#numbers-are-random)을 입력하십시오. –

답변

10

여러 가지 방법이 있습니다.

[Fact] 
public void ImperativeTest() 
{ 
    var fixture = new Fixture(); 
    var expected = fixture.CreateMany<SyncItem>(3).OrderBy(si => si.Key); 
    var unorderedItems = expected.Skip(1).Concat(expected.Take(1)).ToArray(); 
    fixture.Inject(unorderedItems); 

    var sut = fixture.Create<SyncItemList>(); 

    Assert.Equal(expected, sut); 
} 

the default number of many items is 3 동안, 나는 그것이이 테스트 케이스에 명시 적으로 그것을 밖으로 전화를하는 것이 좋습니다 생각 :

명령형 버전

다음은 OP에서 제공하는 것보다 더 간단 필수적 버전입니다. 여기에서 사용 된 스크램블링 알고리즘은 이후에의 순서로 세 개의 (구별되는) 요소의 순서를 지정하고, 첫 번째 요소를 뒤쪽으로 이동하면 이되어야 정렬되지 않은 목록이됩니다.

그러나이 접근법의 문제점은 fixture의 변이에 의존하기 때문에보다 선언적인 방식으로 리팩터링하기가 어렵습니다.(

public class UnorderedSyncItems : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customizations.Add(new UnorderedSyncItemsGenerator()); 
    } 

    private class UnorderedSyncItemsGenerator : ISpecimenBuilder 
    { 
     public object Create(object request, ISpecimenContext context) 
     { 
      var t = request as Type; 
      if (t == null || 
       t != typeof(SyncItem[])) 
       return new NoSpecimen(request); 

      var items = ((IEnumerable)context 
       .Resolve(new FiniteSequenceRequest(typeof(SyncItem), 3))) 
       .Cast<SyncItem>(); 
      return items.Skip(1).Concat(items.Take(1)).ToArray(); 
     } 
    } 
} 

new FiniteSequenceRequest(typeof(SyncItem), 3)) 단순히 약한 타입입니다 해결 : 더 선언적 버전으로 리팩토링하기위한 노력의 일환으로

사용자 정의 버전

는 먼저 a Customization에서 스크램블링 알고리즘을 캡슐화 할 수 있습니다 비 - 일반) SyncItem 인스턴스의 유한 시퀀스를 만드는 방법; 그것은 무엇이 CreateMany<SyncItem>(3) 뒤에 무엇입니까.

[Fact] 
public void ImperativeTestWithCustomization() 
{ 
    var fixture = new Fixture().Customize(new UnorderedSyncItems()); 
    var expected = fixture.Freeze<SyncItem[]>().OrderBy(si => si.Key); 

    var sut = fixture.Create<SyncItemList>(); 

    Assert.Equal(expected, sut); 
} 

을 주목 Freeze 방법의 사용 :

이에 테스트를 리팩토링 할 수 있습니다. UnorderedSyncItems 사용자 지정은 SyncItem[] 인스턴스가 생성되는 방식 만 변경하기 때문에 필요합니다. 그렇게 할 요청을받을 때마다 여전히 새로운 배열을 생성합니다. Freezefixturesut 인스턴스를 생성 할 때마다 동일한 배열이 매번 다시 사용되도록합니다.

은 협약 기반 테스트 위의 테스트가 [UnorderedConventions] 속성의 도입으로, 선언, 규칙 기반의 테스트를 리팩토링 할 수

는 :

public class UnorderedConventionsAttribute : AutoDataAttribute 
{ 
    public UnorderedConventionsAttribute() 
     : base(new Fixture().Customize(new UnorderedSyncItems())) 
    { 
    } 
} 

이것은 단순히 선언적 접착제입니다 UnorderedSyncItems 사용자 지정을 적용합니다. 시험은 지금된다 :

[Theory, UnorderedConventions] 
public void ConventionBasedTest(
    [Frozen]SyncItem[] unorderedItems, 
    SyncItemList sut) 
{ 
    var expected = unorderedItems.OrderBy(si => si.Key); 
    Assert.Equal(expected, sut); 
} 

주목하라 [UnorderedSyncItems][Frozen] 속성의 사용.

이 테스트는 매우 간결하지만 나중에 수행 한 테스트가 아닐 수 있습니다. 문제는 행동의 변경이 이제 [UnorderedSyncItems] 속성에 숨겨져있어 상황이 암묵적이라는 것입니다. 나는 동일한 사용자 정의을 전체 테스트 스위트에 대한 규칙 세트로 사용하는 것을 선호하므로이 레벨에서 테스트 케이스 변형을 소개하고 싶지 않습니다. 그러나 규범에 따라 SyncItem[] 인스턴스가 이어야하며 항상이어야합니다. 그러면이 규칙이 양호합니다.

그러나 일부 경우에만 테스트 케이스에 정렬되지 않은 배열을 사용하려는 경우 [AutoData] 속성을 사용하는 것이 가장 적합한 방법이 아닙니다.

선언적 테스트 케이스

당신은 단순히 그냥 [Frozen] 속성과 같은 매개 변수 레벨 속성을 적용 할 수 있다면 그것은 좋은 것 - 아마도 [Unordered][Frozen]처럼 그들을 결합. 그러나이 방법은 효과가 없습니다.

주문이 인 이전 예에서 유의하십시오. 동결되기 전에 UnorderedSyncItems을 적용해야합니다. 그렇지 않으면 동결되는 배열의 정렬이 보장되지 않을 수 있기 때문입니다.

[Unordered][Frozen] 매개 변수 수준 특성의 문제점은 컴파일하는 동안 .NET Framework에서 AutoFixture xUnit.net 글루 라이브러리가 특성을 읽고 적용 할 때 특성 순서를 보장하지 않는다는 것입니다.

대신에, 당신은 다음과 같이 적용 할 하나의 속성을 정의 할 수 있습니다

public class UnorderedFrozenAttribute : CustomizeAttribute 
{ 
    public override ICustomization GetCustomization(ParameterInfo parameter) 
    { 
     return new CompositeCustomization(    
      new UnorderedSyncItems(), 
      new FreezingCustomization(parameter.ParameterType)); 
    } 
} 

을 (FreezingCustomization[Frozen] 속성의 기본 구현을 제공합니다.)

을이이 테스트를 작성 할 수 있습니다 :

[Theory, AutoData] 
public void DeclarativeTest(
    [UnorderedFrozen]SyncItem[] unorderedItems, 
    SyncItemList sut) 
{ 
    var expected = unorderedItems.OrderBy(si => si.Key); 
    Assert.Equal(expected, sut); 
} 

이 선언 테스트에서는 Customice 없이도 기본 [AutoData] 특성을 사용합니다 왜냐하면 스크램블은 매개 변수 수준에서 [UnorderedFrozen] 속성에 적용되기 때문입니다.

이렇게하면 [AutoData] 유도 속성에 캡슐화 된 (다른) 테스트 스위트 전체 규칙을 사용할 수 있으며 여전히 옵트 인 메커니즘으로 [UnorderedFrozen]을 사용합니다.

+2

와우, 마크,이 대답에 대한 투자에 감사드립니다. 진행 과정은 자동 필링에 대한 레이어를 이해하는 데 정말로 가치가 있으며, 필자가 다루는 시나리오의 각 종류에서 내가 원하는 것을 실제로 얻기 위해 필링을 다시 시작할 수있는 방법이 있습니다. 명성과 감사합니다! – cocogorilla