2017-09-29 54 views
1

我正在经历一个困难的时间,弄清楚如何解决单元测试问题。我很少或没有'单元测试'的经验。我试图只在绝对必要的情况下用最小的更改来更改classUnderTest如何Java单元测试一个复杂的类

我使用的是JUnit 4,我愿意尝试使用MockitoJMockit或任何其他有助于进行高效且有效的单元测试的库。我正在为数据库使用JDBC

问题:

classUnderTest,我访问静态数据库。

SpecializedDao specializedDao = SpecializedDao.getSpecializedDao(); 
ObjectX objectResult = specializedDao.currentObjectXByTimestamp(x, x, x, x, x, x); 

我试图用一个有效的配置和一个无效的配置做单元测试用例。解决这个问题的正确方法是什么?

一些可能解决方案,我已经研究有:

  1. 不知怎么传递,或者“注射”假ObjectXResult类_对象
  2. 模拟数据库(如果甚至有可能与静数据库引用,使用单独的单元测试servlet引用一个假的SpecializedDao类?)
  3. 使用MockitoinjectMocks,间谍,当(方法调用参数)然后返回结果,或一些其他实现Mockito规定。
  4. 开放给任何其他解决方案。

问题:

classUnderTest,我使用的另一项复杂的处理类class2,做了很多工作,像这样:

result = class2.execute(x, x, x, x, x, x); 

类class2做处理的东西,并返回结果ENUM或一些例外。我该如何处理这个问题,并在classUnderTest上保留此特定单元测试的范围class2访问数据库,并做了很多工作,这就是为什么它是它自己的类,但我认为最后三个测试用例依赖于处理以彻底测试class classUnderTest

感谢您对我的支持,我尽可能以尽可能清晰的方式提出问题。

+1

这听起来像你有几层在这里测试。在我的组织中,我们使用像服务层(称为dao层)和dao层(实际上调用jdbc调用)的体系结构。就单元测试而言,要考虑按班级分开你的班级。因此,对于执行实际“业务逻辑”的服务层,您可以使用EasyMock等模拟DAO调用。这将允许您将特定逻辑与服务层隔离开来。对于你的DAO层,你可以考虑使用类似嵌入式数据库(DERBY)的东西来测试 – Jason

+0

如果你绝对不能改变类,PowerMockito允许你模拟静态方法。但我倾向于认为使用PowerMockito作为设计失败的必要性 - 可测试性应该首先是设计标准之一。 –

回答

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等)

3

在任何情况下,你都需要使用类似Mockito的东西,所以我会假设你有这个。

第一个问题:

测试代码,分为静态调用可以是一个痛苦。你可以包含一些字节码操作库,如PowerMock,但在我看来这并不值得。你可以做的是将静态调用放在包本地方法中,然后使用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(); 

问题二:

如果你发现你的测试变得非常复杂,这可能是由于在测试太复杂的类。看看您是否可以将功能提取到其他较小的类中。然后,您只需验证是否正在调用这些较小的类。

+0

好的,我会尽力实现这个。你打算输入SpecializedDao而不是SpecializedDato吗?或者是数据访问测试对象,SpecializedDao的模拟? – weteamsteve

+0

是的..更新的答案.. – Tobb

3

一个很好的规则是永远不要连接到JUnits的外部源。 任何事情,比如数据库连接,你都可以用Mockito来嘲笑他们。

使用Mockito,你可以模拟你不需要测试的所有课程。在你的情况下,你可以嘲笑那些沉重的课程并返回预期的结果。

+1

如果Mockito不适合你,也有JMockit –

1

在我的办公室,我们通常使用一些解决方案来使复杂的类更具可测试性。

最不可取的是只有一个能够在没有修改被测试类的情况下工作 - 这是一种严重的嘲讽。你嘲笑你课外的一切。如果你在课堂上有一个静态成员,你可以在开始之前将它设置为模拟反射(可能是你的问题的答案)。

这种方法很难并且很脆弱,但如果你不能修改被测试的类,它几乎是唯一可行的方法。

一个小异常是一个稍微不同的模拟版本 - 扩展了被测试的类并覆盖了你的类与之交互的所有东西。这对于静态效果并不总是有效,但如果你的类是使用getter设计的,那么效果会很好。

如果你可以修改你的类,我们使用的一些技巧,以帮助:

1)避免静态所有的时间。如果你必须有一个单身人士,请确保它可以通过使用注射框架或自己构建一个迷你注射框架进行测试(一个静态的预填充地图应该是一个好开始)

2)我们把很多我们纯函数中的业务逻辑,我们称之为包含所有这些逻辑“规则”类的类。我们的规则通常可以直接追踪到需求,并且它们不提供任何自己的数据,所有的数据都被传入。它们通常由包含相关数据和其他代码的OO外观调用。

3)减少类的复杂性和相互作用。将数据库访问与计算分开。看看每个班级,并确保它确实做到了一件事。这不会直接帮助,但您会注意到,如果遵循此规则,编写测试会更简单。

1

在模拟框架上没有针对性的解决方案,处理仅涉及CuT最小改变的静态的标准模式是提取静态方法以坐在接口后面,并且最初访问静态方法的类现在访问方法在接口上。这个接口的一个实例被注入到CuT中,当不进行测试时,它是一个小包装类,它直接将代理直接转换为原始静态方法。

现在你有一个接口可以使用,你可以通过setter或构造函数注入来注入这个接口的实例,Bob是你的叔叔。

这个标准的技巧确实需要一些额外的“东西” - 一个接口和一个实现接口的小包装类,它委托给静态接口,以及一些代码或DI框架接线,以实现在生产部分的类通过构造函数或setter注入到CuT中的代码。

然后在你的单元测试中,你有一个钩子来注入一个模拟或者手工创建的存根,或者任何你想要的测试样式。

这是一个非常常见的模式,当处理一个第三方库超出你的控制,喜欢使用静态。