繁体   English   中英

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

[英]Mockito: Inject real objects into private @Autowired fields

我正在使用 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)。 这是可能的还是仅限于注入 Mocks 的机制?

使用@Spy注释

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

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

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

Mockito-home

@间谍

@Mock

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

    /* ... */
}

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

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

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

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


@Spy
..
@Mock
..

@InjectMock
Foo foo;

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM