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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20270391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 00:44:31  来源:igfitidea点击:

Mockito: Inject real objects into private @Autowired fields

javaspringmockito

提问by user2286693

I'm using Mockito's @Mockand @InjectMocksannotations to inject dependencies into private fields which are annotated with Spring's @Autowired:

我正在使用 Mockito@Mock@InjectMocks注释将依赖项注入到用 Spring 注释的私有字段中@Autowired

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

and

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Now I would like to also inject realobjects into private @Autowiredfields (without setters). Is this possible or is the mechanism limited to injecting Mocks only?

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

采纳答案by Dev Blanked

Use @Spyannotation

使用@Spy注解

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito will consider all fields having @Mockor @Spyannotation as potential candidates to be injected into the instance annotated with @InjectMocksannotation. In the above case 'RealServiceImpl'instance will get injected into the 'demo'

Mockito 会将所有具有@Mock@Spy注释的字段视为要注入到用@InjectMocks注释注释的实例中的潜在候选者。在上述情况下,'RealServiceImpl'实例将被注入到“演示”中

For more details refer

有关更多详细信息,请参阅

Mockito-home

Mockito-home

@Spy

@间谍

@Mock

@嘲笑

回答by Yoaz Menda

In Addition to @Dev Blanked answer, if you want to use an existing bean that was created by Spring the code can be modified to:

除了@Dev Blanked 答案之外,如果您想使用由 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);
    }

    /* ... */
}

This way you don't need to change your code (add another constructor) just to make the tests work.

这样你就不需要为了让测试工作而改变你的代码(添加另一个构造函数)。

回答by davidxxx

Mockito is not a DI framework and even DI frameworks encourage constructor injections over field injections.
So you just declare a constructor to set dependencies of the class under test :

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

@Mock
private SomeService serviceMock;

private Demo demo;

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

Using Mockito spyfor the general case is a terrible advise. It makes the test class brittle, not straight and error prone : What is really mocked ? What is really tested ?
@InjectMocksand @Spyalso hurts the overall design since it encourages bloated classes and mixed responsibilities in the classes.
Please read the spy()javadocbefore using that blindly (emphasis is not mine) :

spy在一般情况下使用 Mockito是一个糟糕的建议。它使测试类变得脆弱,不直接且容易出错:真正嘲笑的是什么?什么是真正的测试?
@InjectMocks并且@Spy还会损害整体设计,因为它鼓励类中的臃肿类和混合职责。
请在盲目使用之前阅读spy()javadoc(重点不是我的):

Creates a spy of the real object. The spy calls realmethods unless they are stubbed. Real spies should be used carefully and occasionally, for example when dealing with legacy code.

As usual you are going to read the partial mock warning: Object oriented programming tackles complexity by dividing the complexity into separate, specific, SRPy objects. How does partial mock fit into this paradigm? Well, it just doesn't... Partial mock usually means that the complexity has been moved to a different method on the same object. In most cases, this is not the way you want to design your application.

However, there are rare cases when partial mocks come handy: dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.) However, I wouldn't use partial mocks for new, test-driven & well-designed code.

创建真实对象的间谍。间谍调用真正的方法,除非它们被存根。真正的间谍应该谨慎使用,偶尔使用,例如在处理遗留代码时。

像往常一样,您将阅读partial mock warning:面向对象的编程通过将复杂性划分为单独的、特定的 SRPy 对象来解决复杂性。部分模拟如何适应这种范式?嗯,它只是没有......部分模拟通常意味着复杂性已转移到同一对象上的不同方法。在大多数情况下,这不是您想要设计应用程序的方式。

但是,在极少数情况下,部分模拟会派上用场:处理您无法轻松更改的代码(第 3 方接口、遗留代码的临时重构等)但是,我不会将部分模拟用于新的、测试驱动的 & 以及-设计的代码。

回答by Jonathan S. Fisher

I know this is an old question, but we were faced with the same problem when trying to inject Strings. So we invented a JUnit5/Mockito extension that does exactly what you want: https://github.com/exabrial/mockito-object-injection

我知道这是一个老问题,但我们在尝试注入字符串时遇到了同样的问题。所以我们发明了一个 JUnit5/Mockito 扩展,它完全符合你的要求:https: //github.com/exabrial/mockito-object-injection

EDIT:

编辑:

@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();
 }

回答by takacsot

In Spring there is a dedicated utility called ReflectionTestUtilsfor this purpose. Take the specific instance and inject into the the field.

在 Spring 中有一个专门ReflectionTestUtils用于此目的的实用程序。取具体实例并注入该领域。


@Spy
..
@Mock
..

@InjectMock
Foo foo;

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