[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);
public class DateTimeGenerator { public DateTime Next(); }