如何仅模拟一种注入依赖项的一种方法并正常注入其余方法?

Spe*_*pen 5 java unit-testing dependency-injection mocking mockito

我正在为一个大量使用依赖注入的项目编写测试。

所以通常我只会注入我正在测试的对象:

public class RegistrationTest
        extends WithApplication {

    private RegistrationController controller;

    @Before
    public void setUp() throws Exception {
        Injector injector = app.injector();
        controller = injector.instanceOf(RegistrationController.class);
    }

    @Test
    public void openRegistrationView() {
        Result result = controller.registrationForm();
        assertEquals(OK, result.status());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我需要模拟被测类的依赖项之一的方法的返回值。

为此,我可以使用@InjectMocks@RunWith(MockitoJUnitRunner.class)注入模拟依赖项。

@RunWith(MockitoJUnitRunner.class)
public class RegistrationTest
        extends WithApplication {

    @InjectMocks
    private RegistrationController controller;
    @Mock
    private FormFactory formFactory;
    @Mock
    private RegistrationIpCache registrationIpCache;

    @Test
    public void openRegistrationView() {
        doReturn("test").when(registrationIpCache).getLast();
        Result result = controller.registrationForm();
        assertEquals(OK, result.status());
    }
}
Run Code Online (Sandbox Code Playgroud)

不过,虽然我只是想嘲弄一个方法调用的RegistrationIpCache我需要模拟全班同学,也是我需要模拟的所有其他依赖RegistrationController就像FormFactory在我的例子。现在所有模拟类的每个方法都不能按预期工作。

当然我可以doCallRealMethod()用来模拟所有依赖项的每个方法

doCallRealMethod().when(formFactory).form(Registration.class);
Run Code Online (Sandbox Code Playgroud)

但是正如您已经可以想象的那样,此工作所需的工作量和样板代码数量是极端的。

是不是可以正常注入我的类然后只监视一个依赖项或模拟一个依赖项的一种方法?

Ser*_*hyr 5

您需要使用@Spy而不是@Mock.

@InjectMocks
private RegistrationController controller;
@Mock
private FormFactory formFactory;
@Spy
private RegistrationIpCache registrationIpCache;
Run Code Online (Sandbox Code Playgroud)

但请注意,在这种情况下@Spy将尝试使用默认构造函数。如果默认构造函数不可用,请使用显式构造函数调用:

@Spy
private RegistrationIpCache registrationIpCache = new RegistrationIpCache(dependencies);
Run Code Online (Sandbox Code Playgroud)

以下是 Mockito 无法从@Spy javadoc实例化您的类的情况:

Mockito 将尝试找到零参数构造函数(甚至是私有的)并为您创建一个实例。但是Mockito不能实例化内部类、本地类、抽象类和接口。

以下是手动注入依赖项的示例:

@RunWith(MockitoJUnitRunner.class)
public class RegistrationTest {
    private RegistrationController controller;

    private RegistrationIpCache spyRegistrationIpCache; //this is the dependencies that you need to spy

    @Before
    public void setUp() throws Exception {
        spyRegistrationIpCache = spy(realInstanceOfregistrationIpCache);

        controller = new RegistrationController(registrationIpCache, realInstanceOfFormFactory);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您需要编写更多代码,但这使您可以更好地控制对象实例化和依赖项注入。请注意,不需要使用构造函数实例化。由于您手动执行此操作,因此您可以根据需要实例化它。