Mockito:将真实对象注入私有的@Autowired字段

时间:2013-11-28 15:59:08

标签: java spring mockito

我正在使用Mockito的@Mock@InjectMocks注释将依赖项注入私有字段,这些字段使用Spring @Autowired进行注释:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

现在我想将真正的对象注入私有@Autowired字段(没有setter)。这是可能的还是机制仅限于注射模拟?

4 个答案:

答案 0 :(得分:245)

使用@Spy注释

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito会将所有具有@Mock@Spy注释的字段视为注入@InjectMocks注释注释实例的潜在候选项。在上面的例子中,'RealServiceImpl'实例将被注入'demo'

有关详细信息,请参阅

Mockito-home

@Spy

@Mock

答案 1 :(得分:2)

在Spring中,有一个专用的实用程序ReflectionTestUtils用于此目的。以特定实例为例,并注入到字段中。


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}

答案 2 :(得分:1)

Mockito不是DI框架,甚至DI框架也鼓励构造函数注入而不是字段注入。
因此,您只需声明一个构造函数即可设置被测类的依赖项:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

在一般情况下使用Mockito spy是一个糟糕的建议。它使测试类变脆,不简单并且容易出错:真正被嘲笑的是什么?真正经过测试的是什么?
@InjectMocks@Spy还会损害整体设计,因为它会鼓励classes肿的班级和班级中的混合职责。
read the spy() javadoc,请先盲目使用(重点不是我的):

  

创建真实对象的间谍。间谍会调用 real 方法,除非   他们被打断了。真正的间谍应该小心使用,并且   ,例如在处理旧版代码时。

     

与往常一样,您将阅读partial mock warning:对象   面向程序设计通过划分复杂度来解决复杂度   分成单独的特定SRPy对象。部分模拟如何适应   这个范式?好吧,事实并非如此……部分模拟通常意味着   复杂性已移至同一方法上的另一种方法   宾语。在大多数情况下,这不是您要设计自己的方式   应用。

     

但是,在少数情况下,局部模拟会派上用场:   使用代码,您无法轻易更改(第三方接口,临时   重构旧代码等。)但是,我不会使用部分模拟   用于新的,测试驱动的和精心设计的代码。

答案 3 :(得分:1)

我知道这是一个古老的问题,但是在尝试注入字符串时我们遇到了同样的问题。因此,我们发明了一种JUnit5 / Mockito扩展,可以完全满足您的需求:https://github.com/exabrial/mockito-object-injection

编辑:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }