与未开发的项目相比,维护项目并不有趣,但可以肯定的是,此项目提供了大部分内容。 这个星期我没有检查生产代码,而是测试了。 在测试中看到的内容揭示了生产代码本身的大部分编写方式。 这是一种以更少的风险使事情变得更好的方法。
起初,我只想删除尽可能多的PowerMock使用。 然后我发现大多数Mockito间谍都是不必要的。 然后我发现Mockito模拟是在同一文件中以3种不同的方式初始化的。 然后用法都given()
和when()
在同一个班级,然后用法when()
和doReturn()
在同一类......而且很多可以改进的更多领域。
在本文中,我将限制自己总结一下在测试类中初始化模拟的3种方法:选择并选择最喜欢的一种,但请在您的类中坚持使用(如果不是您的项目)。 一致性是可维护性的Struts之一。
提醒
单元测试是软件质量的重要基础(但不是唯一的基础!)。 由于面向对象的设计是使多个组件各自承担职责,因此确保每个组件以适当的方式执行其任务至关重要。 因此,我们必须使用已知的输入来输入组件,并检查输出的有效性。 因此,孤立地测试组件需要一种方法来用我们控制范围内的输入提供者和输出接收者替换依赖项。 Mockito是一个这样的框架(可以帮助您实现)。
首先,可以使用不同的方式来创建这些模拟(我将使用Mockito措辞代替标准方式 )。
一对一的显式模拟
第一种也是最直接的方法是使用Mockito的mock()
静态方法。
publicclassFooTest{
privateFoofoo;
@Before
publicvoidsetUp(){
foo=Mockito.mock(Foo.class);
}
@Test
publicvoidtest_foo(){
Barbar=Mockito.mock(Bar.class);
// Testing code go there
}
}
它可能是最冗长的,但也是最容易理解的,因为API非常明确。 另外,它不依赖于测试框架。 您可能会嘲笑不利的一面是,如果您有大量依赖关系,则需要一个一个地初始化它们-但是也许有这么高的依赖关系表明设计不好?
多合一通用模拟通话
第二个选项旨在解决此问题(呼叫次数,而不是设计...)。 它用一个模拟所有必需属性的单个调用替换所有方法调用。 为了确定哪个属性应该是模拟的,必须使用@Mock
注释。
publicclassFooTest{
@MockprivateFoofoo;
@MockprivateBarbar;
@Before
publicvoidsetUp(){
MockitoAnnotations.initMocks();
}
@Test
publicvoidtest_foo(){
// Testing code go there
}
}
在这里,将2个Mockito.mock()调用(或更多)替换为单个调用。 不利的一面是,必须将局部变量移至属性,以使Mockito注入模拟。 此外,您必须了解initMocks()
方法的工作方式。
JUnit运行器
最后一个选项用initMocks()
替换显式的initMocks()
调用。
@RunWith(MockitoJUnitRunner.class)
publicclassFooTest{
@MockprivateFoofoo;
@MockprivateBarbar;
@Test
publicvoidtest_foo(){
// Testing code go there
}
}
此选项减轻了编写单个方法调用的麻烦。 但是它具有选项2的缺点,此外,它还可以将您绑定到JUnit,而没有其他替代方法,例如TestNG。
结论
作为维护者,我的偏好按降序排列:
-
显式方法调用
-
因为它们是最明确的,因此可读性强
initMocks
-
因为它可能掩盖了您有太多依赖关系的事实......并且不得不用属性替换局部变量很糟糕
亚军
-
因为它甚至比上一个更糟。 您没有阅读我对TestNG的所有声明吗?
但是最重要的一点是,无论您选择什么选项,都必须在同一个类中坚持使用-如果不是同一个项目。 没有什么比缺乏一致性使代码更难以阅读了。
翻译自: https://blog.frankel.ch/initializing-your-mockito-mocks/