【问题标题】:Extending a class with final attributes. Mockup扩展具有最终属性的类。小样
【发布时间】:2013-02-13 03:42:03
【问题描述】:

我通常将类的每个属性设置为 final(仅适用于将在构造函数中初始化的属性)。 关键是我现在正在实现一个对象的模型以用于测试目的。这个 Mockup 扩展了它正在模拟的类,并且这个类有一些最终属性。因此我不得不在 Mockup 对象的构造函数中调用 super() 构造函数。然而,这破坏了 Mockup 的实用性,因为我不希望它以普通类的方式初始化所有属性。我宁愿调用 Mockup 构造函数而不调用 super() 并做任何我想做的事情。

我的问题是:将属性定义为 final 是否是一种好习惯,只要它们会强制您在 Mockup 中调用类构造函数?

编辑:我添加了一些代码。这种情况下的问题是我使用的是单例,我知道在测试时这不是一个好主意,但在这种情况下我无法更改它。所以我的意图不是在 Mockup 中调用这个方法。

public class ReportsDataManager {

private final Map<String, List<String>> translations;

public ReportsDataManager() {
this.translations = GenericUtils.getTranslation();
}

}

【问题讨论】:

  • 你能发布一些 SUT 的代码和预期的测试吗?将导致测试所需状态的公共呼叫/消息(发布到被测班级时)的顺序是什么?您不必在外部操作 SUT 的私有变量。
  • "但在这种情况下我无法更改它。"更改! ;) stackoverflow.com/questions/2925459/…
  • 被测类是什么样的?它如何使用 ReportsDataManager?
  • 你说得对,我应该改变它,但即使我删除了 Singletone,我仍然会遇到问题。只是猜测 this.translations 以我不喜欢模拟的方式初始化......

标签: java unit-testing mocking final


【解决方案1】:

当你可以做到时,将属性声明为 final 是一个非常好的做法。它赋予不变性 - 保证线程安全。打破它来嘲笑是一个坏主意。你的设计应该服务于用户的需求,而不是你的测试方便。

【讨论】:

  • 我同意你的观点,尽管有时测试(在我看来)应该指导你编写代码的方式。这里的情况是,在构造函数中我调用了另一个给我一个值的方法,但是这个方法很复杂,所以我不想在 Mockup 中调用它。最好的方法是什么?删除最后的条款?在构造函数之外调用这个方法?
  • 在我看来,删除最后一个子句不是可行的方法。我不明白“复杂”是什么意思。如果您已经测试过“复杂”方法并证明它有效,为什么不能模拟它以简单地返回您想要的值并继续使用它?
  • 复杂我的意思是它不仅仅是给属性一个值。我用一段代码编辑了我的问题。在这种情况下,我应该删除 Singleton 并模拟对象 GenericUtils,即方法 getTranslation()。以这种方式它必须工作。但是如果情况不是使用 Singleton 而是调用同一个类的私有方法怎么办。我怎么能摆脱它?我无法覆盖该方法...
【解决方案2】:

如果你想模拟这个类,给它一个接口并模拟这个接口。另外,mocks aren't stubs。听起来您正在创建一个存根,而不是一个模拟。 如果您确实希望创建一个模拟,请选择一个为您生成接口模拟的库。

【讨论】:

  • 我喜欢你的观点,我没想到。只为创建 Mockup 做一个界面真的值得吗?
  • 该接口还将描述两个类是如何耦合的。我认为这总是(嗯,经常)是个好主意。
  • 在这种特殊情况下,这将是解耦对单例的引用的良好第一步。
  • 如何在没有接口的情况下进行模拟?那是没有意义的。
  • 我有时会使用接口创建模拟,但在其他情况下,我通常做的是扩展我想要模拟的类(在这种情况下,它将是:公共类 ReportsDataManagerMockup 扩展 ReportsDataManager)然后覆盖我想要的方法。我想我误解了术语
【解决方案3】:

一般来说,如果您使用的做法使测试代码变得更加困难,那么这种做法可能是一种气味。

尝试通过设置变量final 来确定确切想要实现的目标。 protected 可以接受吗?

【讨论】:

  • 尽管如此 - 我喜欢 @Marko 的 solution 使用反射。
【解决方案4】:

您可以使用标准反射回避final 限制。由于您处于模型的上下文中,因此我想这不会造成太大问题。请注意多线程问题:JVM 将假定该字段遵守final 语义,并会考虑到这一点进行优化。

【讨论】:

  • 我不太喜欢这个想法,因为通常不建议在单元测试中使用反射,是吗?
  • 我不知道有这样的推荐。无论如何,您认为所有这些模拟框架都使用什么?与运行时字节码生成相比,反射相当温和。
  • 这里有一个关于它的讨论:stackoverflow.com/questions/2811141/… 无论如何,使用反射来创建模拟可能是一种解决方案,但直接使用其中一个已经存在的框架不是更有意义吗?
  • 答案的重点是正确的:如果你通过反射调用代码来测试你的代码,那显然是一种气味。如果您使用它来设置测试夹具,这是您的情况,那很好。
  • 好的,你说服了我,但就我而言,我认为我可以用更简单的方式做到这一点
猜你喜欢
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
  • 2019-11-12
  • 1970-01-01
  • 1970-01-01
  • 2014-03-17
  • 1970-01-01
相关资源
最近更新 更多