2010-04-12 3 views
10

단위 테스트가 많은 데이터 설정으로 부풀어 오르면서 AutoFixture http://autofixture.codeplex.com/을 사용하기 시작했습니다. 내 단위 테스트를 작성하는 것보다 데이터를 설정하는 데 더 많은 시간을 보냈습니다. 여기 내 초기 단위 테스트 (DDD 블루 책에서화물 응용 프로그램 샘플에서 가져온 예) 다음자동 수정 리팩토링

[Test] 
public void should_create_instance_with_correct_ctor_parameters() 
{ 
    var carrierMovements = new List<CarrierMovement>(); 

    var deparureUnLocode1 = new UnLocode("AB44D"); 
    var departureLocation1 = new Location(deparureUnLocode1, "HAMBOURG"); 
    var arrivalUnLocode1 = new UnLocode("XX44D"); 
    var arrivalLocation1 = new Location(arrivalUnLocode1, "TUNIS"); 
    var departureDate1 = new DateTime(2010, 3, 15); 
    var arrivalDate1 = new DateTime(2010, 5, 12); 

    var carrierMovement1 = new CarrierMovement(departureLocation1, arrivalLocation1, departureDate1, arrivalDate1); 

    var deparureUnLocode2 = new UnLocode("CXRET"); 
    var departureLocation2 = new Location(deparureUnLocode2, "GDANSK"); 
    var arrivalUnLocode2 = new UnLocode("ZEZD4"); 
    var arrivalLocation2 = new Location(arrivalUnLocode2, "LE HAVRE"); 
    var departureDate2 = new DateTime(2010, 3, 18); 
    var arrivalDate2 = new DateTime(2010, 3, 31); 

    var carrierMovement2 = new CarrierMovement(departureLocation2, arrivalLocation2, departureDate2, arrivalDate2); 

    carrierMovements.Add(carrierMovement1); 
    carrierMovements.Add(carrierMovement2); 

    new Schedule(carrierMovements).ShouldNotBeNull(); 
} 

처럼 보이는 방법의 예 내가 AutoFixture

[Test] 
public void should_create_instance_with_correct_ctor_parameters_AutoFixture() 
{ 
    var fixture = new Fixture(); 

    fixture.Register(() => new UnLocode(UnLocodeString())); 

    var departureLoc = fixture.CreateAnonymous<Location>(); 
    var arrivalLoc = fixture.CreateAnonymous<Location>(); 
    var departureDateTime = fixture.CreateAnonymous<DateTime>(); 
    var arrivalDateTime = fixture.CreateAnonymous<DateTime>(); 

    fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
     (departure, arrival, departureTime, arrivalTime) => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 

    var carrierMovements = fixture.CreateMany<CarrierMovement>(50).ToList(); 

    fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => new Schedule(carrierMovements)); 

    var schedule = fixture.CreateAnonymous<Schedule>(); 

    schedule.ShouldNotBeNull(); 
} 

private static string UnLocodeString() 
{ 
    var stringBuilder = new StringBuilder(); 

    for (int i = 0; i < 5; i++) 
     stringBuilder.Append(GetRandomUpperCaseCharacter(i)); 

    return stringBuilder.ToString(); 
} 

private static char GetRandomUpperCaseCharacter(int seed) 
{ 
    return ((char)((short)'A' + new Random(seed).Next(26))); 
} 

싶습니다에 그것을 리팩토링을 시도 방법입니다 리팩터링하는 것이 더 좋은 방법인지 알아보십시오. 그것보다 짧고 쉽게하고 싶습니다.

답변

14

초기 시도는 좋아 보이지만 약간 단순화 할 수있는 몇 가지 사항이 있습니다. 모든

첫째, 당신은이를 줄일 수있을 것입니다 : 이것에

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    (departure, arrival, departureTime, arrivalTime) => 
     new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 

:

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    () => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 

당신이 그 다른 변수를 사용하지 않는 때문이다. 그러나 이는 본질적으로 동일한 4 개의 값을 사용하도록 CarrierMovement를 생성하는 것을 잠급니다. 생성 된 각 CarrierMovement는 별도의 인스턴스가되지만 모두 동일한 4 가지 값을 공유하므로 그 의미가 맞는지 궁금합니다. 당신이 carrierM 변수를 사용하지 않기 때문에 위와 같은 맥락에서

, 대신

fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => 
    new Schedule(carrierMovements)); 

당신은

fixture.Register(() => new Schedule(carrierMovements)); 

를 작성할 수 있습니다. 유추 추론은 Func의 반환 유형으로 인해 일정을 등록하고 있음을 알 수 있습니다.

그러나, 일정 생성자는 다음과 같습니다 가정 : 당신은 대신이 같은 carrierMovements 등록 할 수

public Schedule(IEnumerable<CarrierMovement> carrierMovements) 

:

fixture.Register<IEnumerable<CarrierMovement>>(carrierMovements); 

것은 AutoFixture가 자동으로 제대로 일정을 해결하는 데 원인이있다. 이 방법을 사용하면 테스트를 중단하지 않고도 미래에 일정 생성자에 매개 변수를 추가 할 수 있으므로 (AutoFixture가 매개 변수 유형을 해결할 수있는 한) 유지 관리가 용이합니다.

그러나 우리는 등록 이외의 다른 것에 대해 carrierMovements 변수를 실제로 사용하지 않기 때문에이 경우보다 더 잘할 수 있습니다. 우리가 실제로해야 할 일은 AutoFixture에 IEnumerable<CarrierMovement> 인스턴스를 생성하는 방법을 알려주는 것입니다.

fixture.Register(fixture.CreateMany<CarrierMovement>); 

공지 메소드 호출 괄호의 부족 : 당신이 (당신은 안) 수 (50)에 대해 상관하지 않을 경우, 우리는 심지어 같은 방법 그룹 구문을 사용할 수 있습니다 우리는 Func을 등록하고, 그리고 CreateMany<T> 메서드가 IEnumerable<T> 형식의 추론을 반환하므로 나머지를 처리합니다.

그러나 모든 세부 사항입니다.상위 레벨에서는 CarrierMovement 등록을 전혀 고려하지 않는 것이 좋습니다. 이 생성자를 가정하면 :

public CarrierMovement(Location departureLocation, 
    Location arrivalLocation, 
    DateTime departureTime, 
    DateTime arrivalTime) 

자동 완성 기능은 자체적으로 알아낼 수 있어야합니다.

모든 departureLocation 및 arrivalLocation에 대해 새 Location 인스턴스를 만들지 만 원래 테스트에서 수동으로 수행 한 것과는 다르지 않습니다.

시간에 관해서는 기본적으로 AutoFixture는 DateTime.Now을 사용합니다. 적어도 도착 시간이 출발 시간보다 짧지는 않을 것입니다. 그러나 그것들은 동일 할 가능성이 매우 높지만 문제가있는 경우 항상 자동 증가 기능을 등록 할 수 있습니다.

이러한 고려 사항을 감안할 때, 여기에 대안이다 :

public void should_create_instance_with_correct_ctor_parameters_AutoFixture() 
{ 
    var fixture = new Fixture(); 

    fixture.Register(() => new UnLocode(UnLocodeString())); 

    fixture.Register(fixture.CreateMany<CarrierMovement>); 

    var schedule = fixture.CreateAnonymous<Schedule>(); 

    schedule.ShouldNotBeNull(); 
} 

당신이 그것을 등록해야합니다 IList<CarrierMovement>의 문제를 해결하려면.

public Schedule(IList<CarrierMovement> carrierMovements) 

내가 정말 당신이 걸릴 그 API를 변경 재고해야한다고 생각 :

fixture.Register<IList<CarrierMovement>>(() => 
    fixture.CreateMany<CarrierMovement>().ToList()); 

그러나 당신이 물어 때문에, 나는 일정 생성자는 다음과 같습니다 것을 의미 : 여기 한 가지 방법이다 IEnumerable<Carriemovement>. API 설계 관점에서, 생성자를 포함하여 모든 멤버를 통해 콜렉션을 제공하면 멤버가 콜렉션을 수정할 수 있음을 의미합니다 (예 : 추가, 제거 및 지우기 메소드 호출). 그것은 당신이 생성자에서 기대하는 거의 행동이 아니므로 허용하지 마십시오.

위 예제에서 AutoFixture는 모든 Location 개체에 대해 새 값을 자동으로 생성하지만 CPU 속도 때문에 DateTime의 후속 인스턴스가 동일 할 수 있습니다.

증가하는 DateTimes를 원한다면, 호출 될 때마다 반환 된 DateTime을 증가시키는 작은 클래스를 작성할 수 있습니다. 내가 관심있는 독자에 해당 클래스의 구현을 떠날거야,하지만 당신은 다음과 같이 등록합니다 수 :

var dtg = new DateTimeGenerator(); 
fixture.Register(dtg.Next); 

이 API를 가정 (한 번 더 방법 그룹 구문 위의 통지) :

public class DateTimeGenerator 
{ 
    public DateTime Next(); 
} 
+0

귀하의 의견에 감사드립니다. 그러나 약간의 예외가 AutoFixture에 의해 throw되었습니다. Ploeh.AutoFixture.ObjectCreationException : AutoFixture는 Public이 없으므로 System.Collections.Generic.IList'1 [DDDBookingApplication.Domain.Voyage.CarrierMovement] 형식의 인스턴스를 만들 수 없습니다. 건설자. CarrierMovement를 만드는 방법을 말해야한다고 가정합니다. –

+0

나는 모든 instares에 대해 다른 데이터 세트를 갖고 싶습니다. 너 생각이 뭐니? –

+0

모든 세부 정보 주셔서 감사합니다. 테스트가 짧고 통과합니다 :) –