我正在经历一个艰难的时期,想弄清楚如何处理单元测试的问题。我几乎没有“单元测试”经验。我正在尝试更改 classUnderTest ,只有在绝对必要时才进行最小的更改。
我正在使用JUnit 4,我愿意尝试使用Mockito,JMockit或任何其他有助于高效且有效的单元测试的库。我正在为数据库使用 JDBC 。
在 classUnderTest 中,我正在访问 static 数据库。
SpecializedDao specializedDao = SpecializedDao.getSpecializedDao();
ObjectX objectResult = specializedDao.currentObjectXByTimestamp(x, x, x, x, x, x);
我正在尝试使用有效配置和无效配置的单元测试用例。解决这个问题的正确方法是什么。
我研究过的一些可能的解决方案是:
在 classUnderTest 中,我正在使用另一个复杂的处理 class2 ,它可以完成很多工作:
result = class2.execute(x, x, x, x, x, x);
class2 处理内容并返回结果ENUM或一些异常。如何在 classUnderTest 上保留此特定单元测试的范围来处理此问题。 class2 访问数据库,并做了很多工作,这就是为什么它是自己的类,但我认为最后三个测试用例依赖于处理来彻底测试 classUnderTest
感谢您对我的支持,我尽可能清晰地提出问题。
答案 0 :(得分:3)
在任何情况下你都需要使用像Mockito这样的东西,所以我假设你有。
第一个问题:
测试涉及静态调用的代码可能很麻烦。您可以包含一些像PowerMock
这样的字节代码操作库,但在我看来它并不值得。你可以做的是将静态调用放在package-local方法中,然后使用spy
将其存根。类似的东西:
//in class under test:
SpecializedDato getSpecializedDao() {
return SpecializedDao.getSpecializedDao();
}
//in test:
import static org.mockito.Mockito.*;
//...
final SpecializedDao daoMock = mock(SpecializedDao.class);
final ClassUnderTest classUnderTest = spy(new ClassUnderTest());
doReturn(daoMock).when(classUnderTest).getSpecializedDao();
第二个问题:
如果您发现测试变得非常复杂,可能是由于被测试的课程过于复杂。看看你是否可以将功能提取到其他较小的类中。然后,您只需要验证是否正在调用这些较小的类。
答案 1 :(得分:3)
一个好的规则是永远不要从JUnits连接到外部源。 任何可以做的事情,例如数据库连接,你都可以使用Mockito来模拟它们。
使用Mockito,您可以模拟您不关心的所有课程。在你的情况下,你可以模拟那些沉重的类并返回预期的结果。
答案 2 :(得分:1)
在我的办公室,我们通常会使用一些解决方案来使复杂的课程更具可测性。
最不可取的是唯一能够在不修改被测试的类的情况下工作的人 - 重度嘲弄。你嘲笑你班级以外的一切。如果你在课堂上有一个静态成员,你可以在开始之前将它设置为带有反射的模拟(可能是你问题的答案)。
这种方法既困难又脆弱,但如果你不能修改被测试的类,这几乎是唯一可行的方法。
一个小的例外是一个稍微不同的模拟版本 - 扩展测试中的类并覆盖您的类与之交互的所有内容。这对于静力学并不总是有效,但如果你的课程是用吸气剂设计的,那么效果很好。
如果您可以修改课程,我们会使用一些技巧来帮助您:
1)始终避免使用静力学。如果您必须有一个单身,请确保它可以通过使用注射框架或自己构建迷你注射框架来测试(静态预先填充的地图应该是一个好的开始)
2)我们将大量业务逻辑放在纯函数中,并调用包含所有逻辑“规则”类的类。我们的规则通常可以直接追溯到需求,并且它们不提供任何自己的数据,所有内容都被传入。它们通常由包含相关数据和其他代码的OO外观调用。
3)降低课程的复杂性和互动性。将数据库访问与计算分开。看看每个班级,并确保它完成一件事。这没有直接帮助,但你会注意到,如果遵循这条规则,编写测试会更简单。
答案 3 :(得分:1)
如果没有在模拟框架上使用解决方案,处理仅涉及对CuT的最小更改的静态的标准模式是提取静态方法以位于接口后面,并且最初访问静态方法的类现在访问方法在界面上。这个接口的一个实例被注入到CuT中,当没有测试时,它是一个小包装类,它直接委托给原始的静态方法。
现在你有一个可以使用的接口,你可以通过setter或构造函数注入注入该接口的实例,而Bob是你的叔叔。
这个标准技巧确实涉及一些额外的东西" - 一个接口,一个实现委托给静态接口的小包装类,以及一些代码或DI框架接线,它们在代码的生成部分中实现类,通过构造函数或setter注入CuT。
然后在你的单元测试中,你有一个钩子来注入一个模拟,一个手工创建的存根,或者你想要的任何样式的测试双。
这是一种非常常见的模式,当您处理不受控制的第三方库时,它喜欢使用静态。
答案 4 :(得分:1)
我最近在youtube上发布了一段视频,展示了Jim Weaver提到的一些步骤。它是德语,但可能仍然有用。在这个视频中,我介绍了一个接口来克服静态调用数据库访问的问题。
https://www.youtube.com/watch?v=KKYro-HGRyk
如果您无法更改所调用方法的源代码,则第二个视频演示了一种有用的方法。
答案 5 :(得分:1)
如果您关心开发良好,有用的测试,我的建议是不模拟数据库或您的代码可能与之交互的其他复杂类。
相反,请考虑您要测试的代码要提供的特定业务场景,并编写逼真的(即,不用廉价 - 通常是不正确的 - 替换真实的类/组件他们)和有意义的(从现实世界的要求的角度来看)测试每个场景。
那就是说,如果您仍想模拟DAO类,可以使用JMockit进行如下操作:
@Test
public void exampleTest(@Mocked SpecializedDao mockDao) {
ObjectX objectResult = new ObjectX();
new Expectations() {{
mockDao.currentObjectXByTimestamp(anyX, anyY, anyZ); result = objectResult;
}};
// Call the SUT.
}
(anyX
等不是实际的JMockit参数匹配字段,当然 - 实际的是anyInt
,anyString
,any
等。< / p>