Mockito @InjectMocks如何运作?

时间:2013-03-05 16:16:22

标签: spring dependency-injection mockito autowired

这是我的问题:

我有几个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 个答案:

答案 0 :(得分:21)

尼古拉斯的回答几乎是正确的,但不是猜测只看InjectMocks的javadoc,而是包含更多细节;)

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

要获得1.9.5中的快速参考,您需要:

  

标记应在其上进行注射的区域。

     

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

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

         

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

  2.   
  3. 属性设置器注入; 模拟将首先按类型解析,然后,如果存在多个相同类型的属性,则通过属性名称和模拟名称的匹配来解析。< / p>      

    注1:如果你有相同类型(或相同的擦除)的属性,最好用匹配的属性命名所有@Mock带注释的字段,否则Mockito可能会感到困惑并且注入赢了'发生了。

         

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

  4.   
  5. 字段注入; 模拟首先按类型解析,然后,如果有多个相同类型的属性,则匹配字段名称和模拟名称。

         

    注1:如果你有相同类型(或相同的擦除)的字段,最好用匹配的字段命名所有@Mock带注释的字段,否则Mockito可能会感到困惑并且注入赢了'发生了。

         

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

  6.   

答案 1 :(得分:3)

如果您有多个服务并想在基于Spring的环境中用模拟对象替换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();

答案 2 :(得分:1)

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

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

  1. 使用Object注释扫描传递的@Mock的成员变量类。
  2. 对于每一个,创建该类的模拟,并将其设置为该成员。
  3. 使用Object注释扫描传递的@InjectMocks的成员变量类。
  4. 扫描每个找到的成员的类,查找可以使用(2)中创建的模拟对象之一注入的成员(即,该字段是父类/接口,或同一类,如模拟对象声明了类)并将其设置为该成员。

答案 3 :(得分:0)

没关系,在网上看 - InjectMocks注释将@Mock注释作为一个字段处理,并且是静态范围的(类范围的),所以我真的不能保证模拟会转到正确的服务。对于尝试在功能级而不是类级别进行单元测试而言,这有点像是一个思想实验。猜猜我会用Spring自动装配这些东西......