微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – AutoFixture重构

我开始使用AutoFixture http://autofixture.codeplex.com/,因为我的单元测试被大量的数据设置blo肿.我花更多的时间来设置数据,而不是写我的单元测试.以下是我的初始单元测试的样例(来自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,18);
    var arrivalDate2 = new DateTime(2010,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,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,CarrierMovement>(
    (departure,arrivalTime) =>
        new CarrierMovement(departureLoc,arrivalDateTime));

到这个:

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

因为你没有使用这些其他变量.然而,这基本上锁定了CarrierMovement的任何创建来使用相同的四个值.虽然每个创建的CarrierMovement将是一个单独的实例,它们将共享相同的四个值,我不知道这是否是你的意思?

与上述同样,代替

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

你可以写

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

因为你不使用carrierM变量.类型引用将会确定您正在注册一个计划,因为Func的返回类型.

但是,假设Schedule构造函数如下所示:

public Schedule(IEnumerable<CarrierMovement> carrierMovements)

你可以改为刚刚注册载体运动:

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

这将导致AutoFixture自动解析计划正确.这种方法更可维护,因为它允许您在将来添加一个参数到Schedule构造函数,而不会中断测试(只要AutoFixture可以解析参数类型).

但是,在这种情况下,我们可以做得更好,因为我们并不真正使用carrierMovements变量进行注册.我们真正需要做的只是告诉AutoFixture如何创建IEnumerable& CarrierMovement的实例.如果你不关心数字50(你不应该),我们甚至可以使用方法组语法,如下所示:

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

注意缺少方法调用parantheses:我们正在注册一个Func,并且由于CreateMany< T>方法返回IEnumerable< T>类型引用负责其余的.

但是,这些都是细节.在更高层次上,您可能想考虑不注册CarrierMovement.假设这个构造函数

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

自动修复应该能够自己弄清楚.

它将为每个出发位置和到达位置创建一个新的位置实例,但这与原始测试中手动完成的没有什么不同.

当涉及到时代,认情况下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());

但是,由于你问,我暗示了Schedule的构造函数如下所示:

public Schedule(IList<CarrierMovement> carrierMovements)

而且我真的认为你应该重新考虑改变这个API来进行IEnumerable&Carriemovement>.从API设计角度来说,通过任何成员(包括构造函数)提供集合意味着允许成员修改集合(例如通过调用它的添加,删除和清除方法).这不是你从构造函数中期望的行为,所以不要这样做.

AutoFixture会自动为上述示例中的所有Location对象生成新值,但由于cpu的速度,DateTime的后续实例可能相同.

如果你想增加DateTimes,你可以编写一个小的类,每次调用返回的DateTime.我会把这个类的实现留给感兴趣的读者,但是你可以这样注册

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

假设这个API(再次通知上面的方法组语法):

public class DateTimeGenerator
{
    public DateTime Next();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


原文地址:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx 原文发布日期: 9/19/2005 原文已经被 Microsoft 删除了,收集过程中发现很多文章图都不全,那是因为原文的图都不全,所以特收集完整全文。 目录 前言 CLR启动程序
前言 随着近些年微服务的流行,有越来越多的开发者和团队所采纳和使用,它的确提供了很多的优势也解决了很多的问题,但是我们也知道也并不是银弹,提供优势的同时它也给我们的开发人员和团队也带来了很多的挑战。 为了迎接或者采用这些新技术,开发团队需要更加注重一些流程或工具的使用,这样才能更好的适应这些新技术所
最近因为比较忙,好久没有写博客了,这篇主要给大家分享一下PLINQ中的分区。上一篇介绍了并行编程,这边详细介绍一下并行编程中的分区和自定义分区。 先做个假设,假设我们有一个200Mb的文本文件需要读取,怎么样才能做到最优的速度呢?对,很显然就是拆分,把文本文件拆分成很多个小文件,充分利用我们计算机中
在多核CPU在今天和不久的将来,计算机将拥有更多的内核,Microsoft为了利用这个硬件特性,于是在Visual Studio 2010 和 .NET Framework 4的发布及以上版本中,添加了并行编程这个新特性,我想它以后势必会改变我们的开发方式。 在以前或者说现在,我们在并行开发的时候可
c语言输入成绩怎么判断等级
字符型数据在内存中的存储形式是什么
c语言怎么求字符串的长度并输出
c语言函数的三种调用方式是什么
c语言中保留两位小数怎么表示
double的输入格式符是什么