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

Java Mockito:将真实对象注入私有@Autowired字段,java,spring,mockito,Java,Spring,Mockito,我使用Mockito的@Mock和@InjectMocks注释将依赖项注入到私有字段中,这些字段用Spring的@Autowired注释: @RunWith(MockitoJUnitRunner.class) public class DemoTest { @Mock private SomeService service; @InjectMocks private Demo demo; /* ... */ } 及 现在我还想将real对象注入私有@

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

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

    @InjectMocks
    private Demo demo;

    /* ... */
}


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

使用
@Spy
注释

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

MoCito将考虑所有字段有<代码> @模拟<代码> >或代码> @间谍< /COD>注释作为潜在候选被注入到实例中,用“代码> @注解MOCKS < /代码>注释注释。在上述情况下,

'RealServiceImpl'
实例将被注入到“demo”中

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


除了@Dev Blanked answer,如果您想使用由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
也会损害整体设计,因为它会鼓励臃肿的类和类中的混合责任。
请在盲目使用之前(重点不是我的):

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

像往常一样,您将阅读
部分模拟警告
:对象 面向对象编程通过划分复杂性来处理复杂性 分为单独的、特定的SRPy对象。部分模拟如何适应 这种范式?嗯,它只是不。。。部分模拟通常意味着 复杂度已经转移到同一台计算机上的不同方法 对象在大多数情况下,这并不是您想要设计产品的方式 应用程序

然而,很少有部分模拟派上用场的情况:交易 对于代码,您无法轻松更改(第三方接口,临时 但是,我不会使用部分模拟 用于新的、测试驱动的和设计良好的代码


我知道这是一个老问题,但我们在尝试注入字符串时遇到了同样的问题。因此,我们发明了一个JUnit5/Mockito扩展,它完全满足您的需求:

编辑:

@InjectionMap
私有映射injectionMap=新HashMap();
@之前
public void beforeach()引发异常{
injectionMap.put(“securityEnabled”,Boolean.TRUE);
}
@之后
public void afterEach()引发异常{
injectionMap.clear();
}

在Spring中,有一个专门的实用程序,名为
ReflectionTestUtils
。以特定实例为例,将其注入到字段中


@间谍
..
@嘲弄
..
@注射模拟
富富,;
@之前
在()之前无效{
ReflectionTestUtils.setField(foo,“bar”,new BarImpl());//`bar`是私有字段
}

通常,当你在嘲弄事物时,这意味着你不太关心具体的对象;您只关心被模拟对象的行为。也许你想做一个集成测试?或者,你能提供一个理由来解释为什么你希望模拟对象和具体对象生活在一起吗?好吧,我在处理遗留代码,需要很多when(…)。然后返回(…)语句来设置模拟,只是为了防止一些NPE之类的。另一方面,一个真实的物体可以安全地用于此目的。因此,有一个选项可以将真实对象与模拟一起注入,这将非常方便。即使这可能是一种代码气味,我认为在这种特殊情况下是合理的。不要忘记<代码> McKoooNeNo.InMoMcCK(this);<在
@Before
方法中的code>。我知道这与最初的问题没有直接关系,但对于后面的任何人来说,都需要添加它以使其可运行。@Cuga:如果您使用JUnit的Mockito运行程序(
@RunWith(MockitoJUnitRunner.class)
),则不需要行
MockitoAnnotations.initMocks(this)谢谢--我从来都不知道这一点,而且一直都在指定+1:对我有用。。。除了字符串对象。Mockito抱怨:
Mockito无法模拟/监视以下内容:-最终类-匿名类-基元类型
谢谢它也为我工作:)使代理使用mock-else用于实际实现spy@AdrianPronk字符串是我的最后一个类,Mockito不是在注射间谍。但它确实注入了一种模拟。该字段是私有的,没有setter。顺便说一句,不需要
新建RealServiceImpl()
@Spy private SomeService服务在监视之前使用默认构造函数创建一个真实对象。@Aada你能详细说明一下吗?这是从哪个库来的,我只能在org中看到injectmock。mockito@sameer导入org.mockito.InjectMocks;我试过这个。但我从安装程序收到空指针异常method@ascetic652一定要从Spring boot test而不是MockitoJunit开始。虽然你的一些观点可能是正确的,但你没有回答这个问题。如果您想使用“SomeService”的真正实现(这里的实际问题是什么),那么您的代码看起来会是什么样子?将“@Mock”更改为“@Spy”会有所帮助。说到构造函数注入-是的!然后使用它并为您的测试类创建一个构造函数;-)。文本警告使用“部分模拟”,但这个问题是关于使用spy而不使用任何模拟。@der
@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}
@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();
 }