19

这是我的问题:

我有几个 Web 服务类来测试它们是否都从通用服务继承了它们的方法。我认为我可以按功能区域(即三组测试方法,每组依赖于不同的底层 DAO 方法调用)分解测试套件,而不是为每个测试套件编写单元测试。

我建议做的是:

@Mock StateDAO mockedStateDao;
@Mock CountyDAO mockedCountyDao;
@Mock VisitorDAO mockedVisitorDao;

然后调用:

@InjectMocks CountyServiceImpl<County> countyService = new CountyServiceImpl<County>();
@InjectMocks StateServiceImpl<State> stateService = new StateServiceImpl<State>();
@InjectMocks VisitorServiceImpl<Visitor> visitorService = new VisitorServiceImpl<Visitor>();

我如何确定每个 mockedDAO 都会被注入到正确的服务中?自动连接所有三个(而不是使用@InjectMocks)会更容易吗?

我正在使用 Spring、Hibernate 和 Mockito ......

4

4 回答 4

22

好吧,尼古拉斯的回答几乎是正确的,但不要只看InjectMocks的 javadoc ,它包含更多细节;)

对我来说,在一个测试中拥有这么多服务是很奇怪的,作为一个单元测试或作为一个集成测试感觉不正确。在单元测试中这是错误的,因为你有太多的合作者,它看起来不像面向对象(或 SOLID)。在集成测试中,这很奇怪,因为您测试与数据库集成的代码没有模拟它。

对于 1.9.5 中的快速参考,您有:

标记应执行注入的字段。

允许速记模拟和间谍注入。最大限度地减少重复的模拟和间谍注入。Mockito 将尝试仅通过构造函数注入、setter 注入或属性注入按顺序注入模拟,如下所述。如果以下任一策略失败,则 Mockito 不会报告失败;即您必须自己提供依赖项。

  1. 构造函数注入;选择最大的构造函数,然后使用仅在测试中声明的模拟来解析参数。

    注意:如果找不到参数,则传递 null。如果需要不可模拟的类型,则不会发生构造函数注入。在这些情况下,您必须自己满足依赖关系。

  2. 属性设置器注入;mocks 会先通过 type 来解析,然后,如果有多个相同类型的 property,则通过 property name 和 mock name 的 match。

    注意 1:如果您有相同类型(或相同擦除)的属性,最好使用匹配属性命名所有 @Mock 注释字段,否则 Mockito 可能会混淆并且不会发生注入。

    注意 2:如果 @InjectMocks 实例之前没有初始化并且有一个无参数的构造函数,那么它将使用这个构造函数进行初始化。

  3. 现场注入;mocks 会先通过 type 来解析,然后,如果有多个相同类型的 property,则通过 field name 和 mock name 的匹配来解析。

    注意 1:如果您有相同类型(或相同擦除)的字段,最好将所有 @Mock 注释字段命名为匹配字段,否则 Mockito 可能会混淆并且不会发生注入。

    注意 2:如果 @InjectMocks 实例之前没有初始化并且有一个无参数的构造函数,那么它将使用这个构造函数进行初始化。

于 2013-03-11T17:45:15.787 回答
3

如果您有多个服务并且想在基于 Spring 的环境中用 Mock-Objects 替换 DAO,我建议使用 Springockito:https ://bitbucket.org/kubek2k/springockito/wiki/Home

这里也提到了: Injecting Mockito mocks into a Spring bean

然后您的 Testclass 可能如下所示:

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (loader = SpringockitoContextLoader.class, locations =    {"classpath:/org/example/package/applicationContext.xml"})
public class NameOfClassTest {

    @Autowired
    @ReplaceWithMock 
    StateDAO mockedStateDao;

    @Autowired
    @ReplaceWithMock 
    CountyDAO mockedCountyDao;

    @Autowired
    @ReplaceWithMock 
    VisitorDAO mockedVisitorDao;

在您的 @Test 或 @Before Methode 中,您可以使用标准的 Mockito 方式设置您的模拟:

Mockito.doReturn(null).when(mockedCountyDao).selectFromDB();
于 2014-02-03T16:18:53.327 回答
1

好吧,静态方法MockitoAnnotations.initMocks(Object)用于引导整个过程。

我不确定它是如何工作的,因为我没有浏览过源代码,但我会像这样实现它:

  1. 使用注释扫描传递Object的类以查找成员变量。@Mock
  2. 对于每一个,创建该类的模拟,并将其设置为该成员。
  3. 使用注释扫描传递Object的类以查找成员变量。@InjectMocks
  4. 扫描每个找到的成员的类,寻找它具有的成员,这些成员可以注入在 (2) 中创建的模拟对象之一(即,该字段是父类/接口,或与声明的模拟对象相同的类类)并将其设置为该成员。
于 2013-03-05T16:36:12.483 回答
0

没关系,在网上查看 - InjectMocks 注释将带有 @Mock 注释的任何内容视为一个字段,并且是静态范围的(类范围),所以我真的不能保证模拟会转到正确的服务。这在某种程度上是一个尝试在功能级别而不是类级别进行单元测试的思想实验。我想我会用 Spring 自动装配这些东西......

于 2013-03-05T16:35:37.563 回答