我开始使用AutoFixture http://autofixture.codeplex.com/因为我的单元测试因大量数据设置而变得臃肿。与编写单元测试相比,我花在设置数据上的时间更多。这是我的初始单元测试的示例(示例取自 DDD 蓝皮书的 cargo 应用程序示例)
[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 的返回类型,类型推断将确定您正在注册一个 Schedule。
但是,假设 Schedule 构造函数如下所示:
public Schedule(IEnumerable<CarrierMovement> carrierMovements)
您可以直接注册 carrierMovements像这样:
fixture.Register<IEnumerable<CarrierMovement>>(carrierMovements);
这会导致 AutoFixture 自动正确解析 Schedule。这种方法更易于维护,因为它允许您在将来向 Schedule 构造函数添加参数而不会破坏测试(只要 AutoFixture 可以解析参数类型)。
但是,在这种情况下我们可以做得更好,因为我们并没有真正使用 carrierMovements除了注册之外的任何变量。我们真正需要做的只是告诉 AutoFixture 如何创建 IEnumerable<CarrierMovement> 的实例。 .如果您不关心数字 50(您不应该关心),我们甚至可以像这样使用 Method Group 语法:
fixture.Register(fixture.CreateMany<CarrierMovement>);
注意缺少方法调用括号:我们正在注册一个 Func,并且由于 CreateMany<T>方法返回 IEnumerable<T>类型推断负责其余的工作。
不过,这些都是细节。在更高的层次上,您可能想要考虑根本不注册 CarrierMovement。假设这个构造函数:
public CarrierMovement(Location departureLocation,
Location arrivalLocation,
DateTime departureTime,
DateTime arrivalTime)
autofixture 应该能够自己解决。
它将为每个 departmentLocation 和 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> 的问题你需要注册它。这是一种方法:
fixture.Register<IList<CarrierMovement>>(() =>
fixture.CreateMany<CarrierMovement>().ToList());
但是,既然你问了,我的意思是 Schedule 构造函数看起来像这样:
public Schedule(IList<CarrierMovement> carrierMovements)
我真的认为您应该重新考虑更改该 API 以获取 IEnumerable<Carriemovement> .从 API 设计的角度来看,通过任何成员(包括构造函数)提供集合意味着允许该成员修改集合(例如,通过调用它的 Add、Remove 和 Clear 方法)。这不是您期望的构造函数的行为,所以不要允许它。
AutoFixture 将自动为所有 Location 生成新值上面示例中的对象,但由于 CPU 的速度,DateTime 的后续实例可能是相同的。
如果您想要增加 DateTimes,您可以编写一个小类,在每次调用时增加返回的 DateTime。我会将该类的实现留给感兴趣的读者,但您可以像这样注册它:
var dtg = new DateTimeGenerator();
fixture.Register(dtg.Next);
假设这个 API(再次注意上面的方法组语法):
public class DateTimeGenerator
{
public DateTime Next();
}
关于c# - AutoFixture重构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2622334/
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我正在使用RubyonRails3.2.2,我想从我的模型/类中“提取”一些方法。也就是说,在不止一个类/模型中,我有一些方法(注意:方法与用户授权相关,并被命名为“CRUD方式”),这些方法实际上是相同的;所以我认为DRY方法是将这些方法放在“共享”模块或类似的东西中。实现该目标的常见且正确的方法是什么?例如,我应该将“共享”代码放在哪里(在哪些目录和文件中)?如何在我的类/模型中包含提到的方法?你有什么建议?注意:我正在寻找“RubyonRails制作东西的方式”。 最佳答案 一种流行的方法是使用ActiveSupport关注点
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
除了可访问性标准不鼓励使用这一事实指向当前页面的链接,我应该怎么做重构以下View代码?#navigation%ul.tabbed-ifcurrent_page?(new_profile_path)%li{:class=>"current_page_item"}=link_tot("new_profile"),new_profile_path-else%li=link_tot("new_profile"),new_profile_path-ifcurrent_page?(profiles_path)%li{:class=>"current_page_item"}=link_tot("p
这个问题在这里已经有了答案:HashsyntaxinRuby[duplicate](1个回答)关闭5年前。我有一个Recipe,其中包含以下未通过lint测试的代码:service'apache'dosupports:status=>true,:restart=>true,:reload=>trueend失败并出现错误:UsethenewRuby1.9hashsyntax.supports:status=>true,:restart=>true,:reload=>true不确定新语法是什么样的...有人可以帮忙吗?
我正在做一个项目。目前我有一个相当大的条件语句,它根据一些输入参数为变量赋值。所以,我有这样的东西。ifsomeconditionx=somevalueelsifanotherconditionx=adifferentvalue...重构它的最佳方法是什么?我希望我最终会得到类似的东西x=somevalueifsomecondition||anothervalueifanothercondition这种事情有规律吗? 最佳答案 只需将赋值放在if之外即可。x=ifsomeconditionsomevalueelsifanotherc
我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume
我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用
什么是Linq聚合方法的ruby等价物。它的工作原理是这样的varfactorial=new[]{1,2,3,4,5}.Aggregate((acc,i)=>acc*i);每次将数组序列中的值传递给lambda时,变量acc都会累积。 最佳答案 这在数学以及几乎所有编程语言中通常称为折叠。它是更普遍的变形概念的一个实例。Ruby从Smalltalk中继承了这个特性的名称,它被称为inject:into:(像aCollectioninject:aStartValueinto:aBlock一样使用。)所以,在Ruby中,它称为inj