我开始使用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)));
}

我想知道,如果有更好的方式来重构。想做到这一更短和更容易。

有帮助吗?

解决方案

您最初的尝试,看起来很好,但至少有几件事情你可以简化一点。

首先,你应该能够减少这样的:

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));

因为你不用那些其他变量。然而,这基本上锁的任何创造CarrierMovement使用相同的四个价值观。尽管每一个建CarrierMovement将是一个单独的实例,他们都将分享相同的四个价值观,我不知道如果这是你什么意思?

在同样作为上,而不是的

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

你可以写

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

因为你不用 carrierM 变量。类型推理会找出你是一个注册的时间表,因为回归类型的Func。

然而,假定的计划的构造是这样的:

public Schedule(IEnumerable<CarrierMovement> carrierMovements)

你可以而不是只有注册的 carrierMovements 是这样的:

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

这将导致AutoFixture自动解决时间表是否正确。这种做法更易于维护的,因为它允许添加一个参数的计划的构造未来,而不破坏试验(如AutoFixture可以解决的参数类型)。

然而,我们可以做得更好,在这种情况下,因为我们真的不用 carrierMovements 变为别的什么比注册。我们真正需要做的只是告诉AutoFixture如何创建的实例 IEnumerable<CarrierMovement>.如果你不关心的数目50(你不应该),我们甚至可以使用的方法小组的语法是这样的:

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

注意到缺乏方法调用parantheses:我们注册的一个函数,并且由于 CreateMany<T> 方法返回 IEnumerable<T> 类型推理需要照顾的其余部分。

然而,所有这些都是细节。在一个更高的水平,你可能想要考虑未注册CarrierMovement。假设这个构造:

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

autofixture应该能够找出本身。

这将创建一个新的位置实例为每departureLocation和arrivalLocation,但是这没有什么不同于什么你的手没有在原始测试。

当涉及到的时间,可通过默认使用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> 你会需要注册。这里的一种方法来做到这一点:

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

然而,由于你问我意味着该计划的构造是这样的:

public Schedule(IList<CarrierMovement> carrierMovements)

我真的觉得你应该重新考虑改变这API采取一个 IEnumerable<Carriemovement>.从API设计的角度来看,提供一个收集过任何成员(包括一个构造)意味着该成员是否允许修改的收集(例如通过援引这是添加、删除和清楚的方法)。这几乎是不行为,你会期望从一个构造的,因此不允许的。

AutoFixture会自动生成新的价值观,为所有 Location 象在我上面的例子,但由于CPU的速度,随后的实例的日期时间很可能相同。

如果你想要增加DateTimes,你可以写一个小类,增加返回日期的每一时间的调用。我会离开实施的这类感兴趣的读者,但是然后您可以登记,像这样:

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

假设这API(通知一旦更多的方法小组的语法上):

public class DateTimeGenerator
{
    public DateTime Next();
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top