在使用 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的真正实现,最终返回所需的值。
另一种选择是使用真实的 FastPowering 实例
https://github.com/mockito/mockito/wiki/Using-Spies-(and-Fakes) https://github.com/mockito/mockito/wiki/Mocking-Object-Creation
一些 stackoverflow 线程概述了差异 模拟框架中的模拟与间谍活动
简短回答:将
@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 方法中创建的无关新实例来修复此问题(请参见下面的示例)。也可以通过在初始化
MockitoAnnotations.initMocks(this)
后移动 myClass
来修复该问题,但由于 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()
方法的end。
如果它位于
setUp()
的顶部,那么可能会导致其他模拟类无法初始化。
我自己刚刚遇到了这个错误,并将
.initMocks
放在我的 @Before
的末尾解决了我的问题。
我想提一件事要检查,发生在我身上的是我调用了错误的注入对象,或者更确切地说,我调用了父类,我应该在其中调用子类,这是我注入的模拟对象中的子类.
还有 2 件事需要检查:
1. Mocking the behaviours of fastPower. What should this mocked object return, when it methods are called? i.e. when(fastPower.doSomething()).thenReturn(some_kind_of_object);
2. Check if the controller.computeAnswer() does not return NULL for the input of new BigDecimal(2), 2)).longValue(), (long) Math.pow(2, 2).