除非设置代码进行测试,否则通常会接受您无法测试代码?
一段假设的代码:
public void QueueOrder(SalesOrder order)
{
if (order.Date < DateTime.Now-20)
throw new Exception("Order is too old to be processed");
...
}
有些人会考虑将其重构为:
protected DateTime MinOrderAge;
{
return DateTime.Now-20;
}
public void QueueOrder(SalesOrder order)
{
if (order.Date < MinOrderAge)
throw new Exception("Order is too old to be processed");
...
}
注意:您可以提出更复杂的解决方案;涉及IClock
接口和工厂。这不会影响我的问题。
更改上述代码的问题是代码已更改。代码已更改,而客户没有要求更改它。任何变化都需要会议和电话会议。所以我正处于不容易测试的地步。
如果我不愿意/能够做出改变:它是否使我无法进行测试?
注意:上面的伪代码可能看起来像C#,但这只是它的可读性。问题是与语言无关。
注意:假设的代码段,问题,重构需求和重构都是假设的。如果你对我的不满,你可以插入你自己的假设代码样本。
注意:上述假设代码是假设的。与任何生活或死亡的代码有任何关系,纯粹是巧合。
注意:代码是假设的,但任何答案都不是。问题不是主观的:因为我相信有答案。
更新:当然,这里的问题是,我无法保证上述示例中的更改不会破坏任何内容。当然,我将一段代码重构为一个单独的方法,并且代码在逻辑上是相同的。
但我无法保证添加新的受保护方法不会偏移对象的虚方法表,如果此类在DLL中,那么我刚刚引入了访问冲突。
答案 0 :(得分:4)
答案是肯定的,有些代码需要更改以使其可测试。
但是可能有很多代码可以在不必更改的情况下进行测试。我会首先专注于为这些东西编写测试,然后在其他客户需求为您提供以可测试的方式重构它时为其余部分编写测试。
答案 1 :(得分:1)
代码可以从一开始就被编写为可测试。如果它从一开始就没有考虑可测试性,你仍然可以测试它,你可能会遇到一些困难。
在您的假设代码中,您可以通过创建具有过去日期的SalesOrder来测试原始代码,或者您可以模拟DateTime.Now。如您所示,重构的代码更适合测试,但并非绝对必要。
答案 2 :(得分:1)
如果您的代码不是为测试而设计的,则测试它会更加困难。在您的示例中,您必须覆盖DateTime.Now方法,这绝对不是一件容易的事。
我认为为代码添加测试没有什么价值,或者不允许更改现有代码,那么你就不应该这样做。
但是,如果您相信TDD,那么您应该编写带有测试的新代码。
答案 3 :(得分:1)
您可以使用Mock对象框架对原始示例进行单元测试。在这种情况下,我会多次模拟SalesOrder对象,每次配置一个不同的Date值,然后进行测试。这样可以避免更改随附的任何代码,并允许您验证有关算法的订单日期过去并不太远。
为了更好地全面了解您正在处理的依赖关系以及您可以使用的语言功能,我建议Working Effective with Legacy Code。
答案 4 :(得分:0)
在一些动态语言中很容易实现。例如,我可以挂钩import / using语句并用stub文件替换实际的依赖项,即使SUT(System Under Test)将它用作隐式依赖项。或者我可以重新定义这些符号(类,方法,函数等)。我不是说这是要走的路。事情应该重构,但是更容易编写一些表征测试。
答案 5 :(得分:0)
这类代码的问题始终是,它正在创建并依赖于许多静态类,框架类型等等。
为所有这些物体“注入”假货的一个非常好的解决方案是Typemock Isolator(它是商业的,但值得每一分钱)。所以是的,你肯定可以测试遗留代码,遗留代码是在没有可测试性的情况下编写的。我在使用Typemock的一个大项目上完成了它并且取得了非常好的结果。
除了Typemock,您可以使用免费的MS Moles框架,它基本相同。只是它有一个非常不直观的API,并且更难学习和使用。
HTH。
托马斯
答案 6 :(得分:0)
Mockito的Mockito + PowerMock。
您可以在不显着更改代码的情况下测试几乎所有内容。但是需要一些制定者来注入嘲笑。