Java 单元测试和太多模拟

Java 单元测试和太多模拟,java,unit-testing,Java,Unit Testing,我开始在我的项目中实践TDD,作为背景,它还包含遗留代码。我们使用Mockito作为模拟框架,并遵循SpringMVC方法 有时会有一个服务类使用许多不同的DAO对象作为@Autowired属性来实现。这些服务中有一些简单的方法,例如completeTransaction completTransaction将使用许多DAO对象来完成其职责 更新并保存事务 推进业务流程 关闭其他挂起的操作 但是,在执行这些操作时,该方法需要调用不同的DAO,以获取和更新事务、获取业务流程ID、获取挂起的事务

我开始在我的项目中实践TDD,作为背景,它还包含遗留代码。我们使用Mockito作为模拟框架,并遵循SpringMVC方法

有时会有一个
服务
类使用许多不同的
DAO
对象作为
@Autowired
属性来实现。这些服务中有一些简单的方法,例如
completeTransaction

completTransaction
将使用许多
DAO
对象来完成其职责

  • 更新并保存事务
  • 推进业务流程
  • 关闭其他挂起的操作
但是,在执行这些操作时,该方法需要调用不同的
DAO
,以获取和更新事务、获取业务流程ID、获取挂起的事务(并保存其更新)。这意味着单元测试这个方法使我添加了许多
@Mock
属性。我需要在测试实际完成之前设置模拟对象,以便测试特定条件

这看起来像是一种代码味道,对我来说,几乎感觉测试是在确保代码的实现,而不仅仅是它的契约。同样,如果不模拟依赖项,测试用例将不会运行(由于NPE和其他原因)

我可以遵循什么策略来清理这样的代码?(不过,我不能提供关于这个问题的实际源代码)。我认为有一种可能是使用类似(“getPendingOperations”和“advanceBusinessProcess”)的方法建立facade类。然后我可以模拟一个依赖项。但是后来我发现,在所有其他有类似情况的类中,我也需要这样做,然后我担心为了更干净的测试,最终会有很多“helper”类


感谢您的高级支持。

如果单元测试正在测试
completeTransaction
方法,那么您必须模拟它所依赖的一切。由于您使用的是Mockito,因此可以使用
verify
确认调用了正确的mocked方法,并且它们的调用顺序正确


如果单元测试正在测试调用
completeTransaction
方法的东西,那么只需模拟
completeTransaction
方法即可

如果这是您的类层次结构:

class A -> class B -> class C A类->B类->C类 (其中->为“取决于”)

在A类的单元测试中,只模拟B类


在B类的单元测试中,只模拟C类。

我认为当你发现自己有太多的模拟时,通常需要做两件事。这些都不是必须的,但你可能会发现它们很有用

1) 尝试将方法和类变小。我认为干净的代码说有两条规则,类应该很小。那个班应该比那个班小。这是有意义的,因为当您测试的单元(方法和类)变小时,依赖关系也会变小。当然,最终会有更多的测试,但每次测试中的设置会更少

2) 看看德米特定律()。有很多规则,但基本上,您希望避免长字符串的属性/方法调用
objA=objB.propertyA.SomeMethod().propertyCobjA=objB.newProperty然后您只需要模拟objB,它是一个属性


这两个都不是灵丹妙药,但希望您能在项目中使用其中的一些想法。

是的,我理解模拟依赖关系的必要性,但我想说的是,有时依赖关系似乎失控了。测试代码最终创建大量的
when()。然后返回()
,只是为了设置测试内容。当设置代码变大时,很难维护测试。当“依赖关系失控”时,问题的根源不是单元测试,而是正在进行单元测试的类。如果您有大量设置的情况,请考虑非测试方法来初始化模拟对象。我建议不要使用非测试方法来确认单元测试的结果,但很高兴地建议将其用于设置;一些用于安装的私有方法使它看起来更容易。我不认为有任何开发人员没有看到这一点,对于遗留代码,我尝试编写测试来测试整个流程,因此我将模拟DAO,并在控制器级别编写测试,允许服务和组件执行它们应该执行的操作。。。