Unit testing 条件编译是单元测试的有效模拟/存根策略吗?

Unit testing 条件编译是单元测试的有效模拟/存根策略吗?,unit-testing,stub,conditional-compilation,Unit Testing,Stub,Conditional Compilation,在最近一个关于存根的问题中,许多答案建议使用C#接口或委托来实现存根,但建议使用条件编译,在生产代码中保留静态绑定。这个答案在阅读时被修改为-2,所以至少有两个人真的认为这是一个错误的答案。原因可能是错误使用调试,或者可能是使用固定值而不是更广泛的验证。但我不禁想知道: 使用条件编译是实现单元测试存根的一种不合适的技术吗?有时?总是吗 谢谢 编辑添加:我想添加一个示例作为实验: class Foo { public Foo() { .. } private DateTime No

在最近一个关于存根的问题中,许多答案建议使用C#接口或委托来实现存根,但建议使用条件编译,在生产代码中保留静态绑定。这个答案在阅读时被修改为-2,所以至少有两个人真的认为这是一个错误的答案。原因可能是错误使用调试,或者可能是使用固定值而不是更广泛的验证。但我不禁想知道:

使用条件编译是实现单元测试存根的一种不合适的技术吗?有时?总是吗

谢谢

编辑添加:我想添加一个示例作为实验:

class Foo {
    public Foo() { .. }
    private DateTime Now { 
      get {
#if UNITTEST_Foo
        return Stub_DateTime.Now;
#else
        return DateTime.Now;
#endif
      }
    }
    // .. rest of Foo members
}
相比

interface IDateTimeStrategy { 
    DateTime Now { get; }
}
class ProductionDateTimeStrategy : IDateTimeStrategy {
  public DateTime Now { get { return DateTime.Now; } }
}
class Foo {
    public Foo() : Foo(new ProductionDateTimeStrategy()) {}
    public Foo(IDateTimeStrategy s) { datetimeStrategy = s; .. }
    private IDateTime_Strategy datetimeStrategy;
    private DateTime Now { get { return datetimeStrategy.Now; } }
}
这使得“DateTime.Now”的传出依赖项可以通过C#接口存根。然而,我们现在添加了一个动态分派调用,其中静态就足够了,对象甚至在生产版本中也更大,并且我们为Foo的构造函数添加了一个新的失败路径(分配可能失败)


我在这里什么都不用担心吗?感谢到目前为止的反馈

我认为这降低了人们查看代码的清晰度。您不必记住特定代码周围有一个条件标记来理解上下文。

不,这很糟糕。它将测试泄漏到您的生产代码中(即使它已关闭)


糟糕。

在大型代码库中进行可测试性重构时,它可能是一个有用的工具。我可以看到您可以如何使用这些技术来实现较小的更改和避免“大爆炸”重构。然而,我会担心过于依赖这种技术,并会努力确保这些技巧不会在代码库中存在太长时间,否则您可能会使应用程序代码变得非常复杂,难以理解

测试代码应该是明显的,而不是与测试代码在同一块中相互混合

这和你不应该写作的原因差不多

if (globals.isTest)

尽量将生产代码与测试代码分开。维护不同的文件夹层次结构。。不同的解决方案/项目


除非。。你在传统C++代码的世界里。一切都来了。。如果条件块帮助您获得一些可测试的代码,并且您看到了一个好处。。一定要这样做。但是尽量不要让它变得比初始状态更混乱。清楚地注释和划分条件块。小心行事。这是在测试工具下获取遗留代码的有效技术。

我想到了另一个可怕的原因:


很多时候,您模拟/存根某些东西,希望它的方法根据您正在测试的内容返回不同的结果。这可能会导致或使它变得笨拙。< /P>你是指“当增量地添加单元测试”或“这是C++中工作”还是“在传统C++中工作,但在启动一个新的C++项目时会被禁止”?或者在你的或序列中的第一个子句时,这对于现有的(遗留的)代码库是可以的。这是一种很好的技术,可以在不太干扰代码库的情况下对遗留代码进行测试(因为没有测试的安全网,所以您对进行这些更改没有信心)。这比使用C#接口更尴尬吗?在这两种情况下,您都有一个用于模拟的方法实现体和一个用于生产行为的方法实现体。您使用过模拟框架吗?你没有额外的身体进行测试。此外,我说的是你想要的情况,比如说,7种不同的行为取决于你的环境。这只是一个可怕的想法。不要这样做。