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

发布于 2024-07-05 19:19:09 字数 1268 浏览 7 评论 0原文

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

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

谢谢。

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

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

与 Which 相比

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; } }
}

,允许通过 C# 接口对“DateTime.Now”的传出依赖进行存根。 然而,我们现在添加了一个动态调度调用,其中静态就足够了,即使在生产版本中对象也更大,并且我们为 Foo 的构造函数添加了一个新的失败路径(分配可能会失败)。

我在这里什么都不用担心吗? 感谢迄今为止的反馈!

In a recent question on stubbing, many answers suggested C# interfaces or delegates for implementing stubs, but one answer suggested using conditional compilation, retaining static binding in the production code. This answer was modded -2 at the time of reading, so at least 2 people really thought this was a wrong answer. Perhaps misuse of DEBUG was the reason, or perhaps use of fixed value instead of more extensive validation. But I can't help wondering:

Is the use of conditional compilation an inappropriate technique for implementing unit test stubs? Sometimes? Always?

Thanks.

Edit-add: I'd like to add an example as a though experiment:

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

comparing to

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; } }
}

Which allows the outgoing dependency on "DateTime.Now" to be stubbed through a C# interface. However, we've now added a dynamic dispatch call where static would suffice, the object is larger even in the production version, and we've added a new failure path for Foo's constructor (allocation can fail).

Am I worrying about nothing here? Thanks for the feedback so far!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

秋叶绚丽 2024-07-12 19:19:09

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

除非..您处于遗留 C++ 代码的世界中。 一切都会发生..如果条件块帮助您获得一些可测试的代码并且您会看到好处..无论如何都要这样做。 但尽量不要让它变得比初始状态更混乱。 清晰地注释和划分条件块。 谨慎行事。 这是一种在测试工具下获取遗留代码的有效技术。

Try to keep production code separate from test code. Maintain different folder hierarchies.. different solutions/projects.

Unless.. you're in the world of legacy C++ Code. Here anything goes.. if conditional blocks help you get some of the code testable and you see a benefit.. By all means do it. But try to not let it get messier than the initial state. Clearly comment and demarcate conditional blocks. Proceed with caution. It is a valid technique for getting legacy code under a test harness.

清欢 2024-07-12 19:19:09

测试代码应该是显而易见的,并且不要与测试代码混合在同一块中。

这与你不应该写的原因几乎相同

if (globals.isTest)

Test code should be obvious and not inter-mixed in the same blocks as the tested code.

This is pretty much the same reason you shouldn't write

if (globals.isTest)
走走停停 2024-07-12 19:19:09

不,这太可怕了。 它将测试泄漏到您的生产代码中(即使其条件关闭)

非常糟糕。

No this is terrible. It leaks test into your production code (even if its conditioned off)

Bad bad.

彻夜缠绵 2024-07-12 19:19:09

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

I think it lessens the clarity for people reviewing the code. You shouldn't have to remember that there's a conditional tag around specific code to understand the context.

尤怨 2024-07-12 19:19:09

我想到了这很糟糕的另一个原因:

很多时候你模拟/存根某些东西,你希望它的方法根据你正在测试的内容返回不同的结果。 这要么排除了这一点,要么让它变得非常尴尬。

I thought of another reason this was terrible:

Many times you mock/stub something, you want its methods to return different results depending on what you're testing. This either precludes that or makes it awkward as all heck.

╰つ倒转 2024-07-12 19:19:09

当您在大型代码库中重构可测试性时,它可能会作为一种有用的工具来依靠。 我可以看到您如何使用此类技术来实现较小的更改并避免“大爆炸”重构。 但是,我会担心过于依赖这种技术,并会尽力确保此类技巧不会在代码库中存在太久,否则您可能会面临使应用程序代码变得非常复杂且难以遵循的风险。

It might be useful as a tool to lean on as you refactor to testability in a large code base. I can see how you might use such techniques to enable smaller changes and avoid a "big bang" refactoring. However I would worry about leaning too hard on such a technique and would try to ensure that such tricks didn't live too long in the code base otherwise you risk making the application code very complex and hard to follow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文