在使用 JUnit 进行单元测试时,我在传递依赖项时遇到了一些麻烦。
请考虑以下代码段:
这是我要测试的类中的依赖注入,我们称之为控制器。
@Inject private FastPowering fastPowering;
这是单元测试:
@RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
@Mock
FastPowering fastPower;
@InjectMocks
Controller controller;
@Test
public void test() {
assertEquals(
(controller.computeAnswer(new BigDecimal(2), 2)).longValue(),
(long) Math.pow(2, 2));
}
}
似乎 fastPower 为空,请解释如何解决。空指针异常,因为在 .computeAnswer 方法中调用@injected字段 (fastPower)
编辑:
解决了我应该读过@Mock和@Spy之间的区别......
由于有很多评论,我正在为解决方案添加更多上下文
不同之处在于,在模拟中,您正在创建一个完整的模拟或假对象,而在间谍中,有真正的对象,您只是监视或存根它的特定方法。当然,在间谍对象中,由于它是一个真正的方法,当你不存根该方法时,那么它将调用真正的方法行为。
如果fastPower被注释为@Mock那么它的方法就是虚拟的,但controller.computeAnswer依赖于它们来计算。一个人必须提供行为。
如果在没有存根的情况下使用间谍,那么正在执行 fastPower 的真正实现,最终返回所需的值。
另一种选择是使用真正的快速供电实例
https://github.com/mockito/mockito/wiki/Using-间谍-(和假货)https://github.com/mockito/mockito/wiki/Mocking-Object-Creation
还有一些堆栈溢出线程概述了模拟框架中的模拟与间谍的区别
简短回答:将@Mock
替换为@Spy
,应该工作正常
使用 MockitoAnnotations.initMocks
启动@Mock
和@InjectMocks
对象。您的测试将如下所示:
@Mock
FastPowering fastPower;
@InjectMocks
Controller controller;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
....
}
我使用了错误的@Test
注释,如果您想在 Mockito 测试中使用 @InjectMocks
和 @Mock
,那么您应该
- 在测试类上添加
@ExtendWith(MockitoExtension.class)
注释 - 使用
@Test (org.junit.jupiter.api.Test)
而不是@Test (org.junit.Test)
注释来批注测试方法。 请小心用于此批注的导入。
这适用于mockito-core:3.6.28
调试后我找到了一个原因。这是因为org.powermock.core.MockRepository#instanceMocks
收藏。它不包含带有@InjectMocks
注释的字段的模拟(在您的情况下Controller controller
)。要解决这个问题,请尝试在字段声明中使用@Spy
注释,并初始化它们并在类声明上方@PrepareForTest
:
@PrepareForTest(Controller.class)
@RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
@Mock
FastPowering fastPower;
@Spy
@InjectMocks
Controller controller = new Controller();
@Test
public void test() {
//...
}
}
就我而言,它有所帮助。不需要使用 Mockitoannotations.initMocks(this)
方法,它不会影响结果。
我通过删除我在 @Before 方法中创建的无关新实例来解决此问题(请参阅下面的示例)。它也通过在初始化myClass
后移动MockitoAnnotations.initMocks(this)
来修复,但由于 Mockito 无论如何都创建了myClass
,因此该解决方案较差。
// Note - you may need @mock(name="foo") to help mockito differentiate props
// if they have the same type
@Mock
private Thing something;
@InjectMocks
private MyClass myClass;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this); // Moving this below the next line fixed it...
myClass = new MyClass() // But just remove this line and let Mockito do the work
}
需要注意的是,MockitoAnnotations.initMocks(this);
的使用需要在@Before
/setUp()
方法结束时使用。
如果它位于setUp()
的顶部,则可能导致其他模拟类无法初始化。
我自己刚刚遇到了这个错误,.initMocks
放在@Before
的末尾解决了我的问题。
还有 2 件事要检查:
- 嘲笑快力的行为。调用此方法时,此模拟对象应返回什么?即 when(fastPower.doSomething()).thenReturn(some_kind_of_object);
- 检查 controller.computeAnswer() 是否没有为新的 BigDecimal(2), 2) 的输入返回 NULL)。longValue(), (long) Math.pow(2, 2).