【问题标题】:Is it a code smell to have a special constructor only used during testing?仅在测试期间使用特殊构造函数是否是代码异味?
【发布时间】:2011-06-05 18:27:41
【问题描述】:

假设我有一个类 Foo,它只使用类 Bar 的实例进行实例化:

public Foo(Bar x) {
    this.a = x.a();
    this.b = x.b();
    ...
}

现在我想测试Foo,进一步假设难以创建具有所需状态的Bar 实例。作为附加约束,a, b, ... 字段被声明为 final,因此该字段的设置器不可用。

一种可能性是在 Foo 中创建一个额外的构造函数:

protected Foo(A a, B b, ...) {
    this.a = a;
    this.b = a;
    ...
}

此构造函数仅在测试期间使用,我将在此构造函数的注释中声明。

问题:这是代码异味吗?

我想到的另一个解决方案是嘲笑Bar。想知道在这种情况下这是否是最佳做法?

【问题讨论】:

  • 如果你需要构造函数仅用于测试你正在测试错误的东西
  • @Op De Cirkel:你能说得更具体点吗?我想测试Foo 并想将其设置为特定状态......那么“错误的事情”是什么?
  • Bar 是接口吗?在这些情况下,使用接口有助于放松耦合并简化测试。
  • @Hovercraft:不,它不是,但我可以改变它:) 如果它是一个接口,那么创建一个模拟实现它会很容易。但它也会引入一个新接口,当仅存在两个实现时,这可能会被视为开销。

标签: java unit-testing testing constructor


【解决方案1】:

Mocking Bar 更有可能被认为是最佳实践。您应该能够创建一个 MockBar 以便您可以这样做

Foo foo = new Foo(new MockBar(a, b));

【讨论】:

  • 你会像 Mike M 提议的那样手动模拟它,还是使用框架?
  • 如果 Bar 接口很复杂,或者您想测试 a()b() 是否按预期调用,我会用框架模拟它。对于一个简单的案例,我只需创建一个简单的 Mock 类。
【解决方案2】:

我可能会认为这种糟糕的形式,但如果它有效,它就有效。一个更好的解决方案是创建一个TestBar 类,它是Bar 的子类,并覆盖Bar 中的方法。正如您在问题中提到的那样,我想这几乎是在嘲笑该对象。它更简洁,允许您定义一个不同的 Bar 实现,可以包含在您的测试驱动程序中。

【讨论】:

    【解决方案3】:

    嗯嗯嗯。

    • 如果问题是 Bars 很难/难以创建,那么请让创建它们变得容易。
    • 为什么你没有一个构造函数,它具有从 Bar 对象中获取的任意数量的参数?它看起来像是一个包装器或道之类的东西。 可以通过使用包装器来避免 20-或 30-arg 调用,但是您的调用者将如何创建包装器(不使用 20-或 30-arg用于创建包装器或使用“return this”设置器的 20 级或 30 级链接的调用)?

    也就是说,您说的是什么测试?如果要测试的东西是单独的方法,或者要测试的东西是我们可以获得的最类似于生产环境中的整个应用程序,那么这对于创建环境所需的内容会产生重大影响对于要测试的东西。 “尽我们所能”意味着您不应执行任何旨在使测试成功的代码。在针对应用程序的较小单个部分的测试中,这不是问题。 “测试”并不总是与“测试”相同。测试汽车的防滚杆意味着测试它是否可以承受它所说的可以承受的扭矩。在测试整辆车时,你不会做同样的事情。测试零件与测试整个装配体不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-16
      • 1970-01-01
      • 2016-12-02
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      • 2012-06-09
      相关资源
      最近更新 更多