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

我使用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字段(不带设置符)。这是可能的还是该机制仅限于注入mock ?

162829 次浏览

使用@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

@间谍

@Mock

除了@Dev blank之外,如果你想使用Spring创建的现有bean,代码可以修改为:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {


@Inject
private ApplicationContext ctx;


@Spy
private SomeService service;


@InjectMocks
private Demo demo;


@Before
public void setUp(){
service = ctx.getBean(SomeService.class);
}


/* ... */
}

通过这种方式,您不需要更改代码(添加另一个构造函数)就可以使测试正常工作。

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

@Mock
private SomeService serviceMock;


private Demo demo;


/* ... */
@BeforeEach
public void beforeEach(){
demo = new Demo(serviceMock);
}
在一般情况下使用Mockito spy是一个糟糕的建议。它使测试类变得脆弱,而不是直接且容易出错:真正被嘲笑的是什么?
@InjectMocks@Spy也会损害整体设计,因为它鼓励臃肿的类和类中的混合职责 请在盲目使用之前读取spy() javadoc(强调不是我的):

创建一个真实对象的间谍。间谍调用真正的方法除非 他们都被打烂了。真正的间谍应该小心翼翼地使用 偶尔,例如在处理遗留代码时 像往常一样,你将读取partial mock warning: Object 面向对象编程通过划分复杂性来处理复杂性 变成独立的、特定的SRPy对象。部分模拟如何适合 这种模式吗?嗯,它只是没有…部分模拟通常意味着 这种复杂性已经转移到不同的方法上了 对象。在大多数情况下,这不是您想要的设计方法 应用程序。< / p > 然而,在很少的情况下,部分模拟可以派上用场:处理 对于代码,您不容易更改(第三方接口,临时 然而,我不会使用部分模拟 对于新的,测试驱动的&精心设计的代码。< / p >

我知道这是一个老问题,但我们在尝试注入string时也面临同样的问题。所以我们发明了一个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();
}

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


@Spy
..
@Mock
..


@InjectMock
Foo foo;


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