如何使用 Mockito 模拟受保护的方法?
How do I use Mockito to mock a protected method?
我正在使用 Mockito 1.9.5。我如何模拟从受保护方法返回的内容?我有这个受保护的方法…
protected JSONObject myMethod(final String param1, final String param2)
{
…
}
但是,当我尝试在 JUnit 中执行此操作时:
final MyService mymock = Mockito.mock(MyService.class, Mockito.CALLS_REAL_METHODS);
final String pararm1 = “param1”;
Mockito.doReturn(myData).when(mymock).myMethod(param1, param2);
在最后一行,我收到编译错误“方法‘myMethod’不可见。”如何使用 Mockito 模拟受保护的方法?如果这是答案,我愿意升级我的版本。
这不是 Mockito 的问题,而是普通旧 java 的问题。从你调用方法的地方,你没有可见性。这就是为什么它是编译时问题而不是 运行 时问题。
几个选项:
- 在与模拟的包相同的包中声明您的测试 class
- 如果可以,请更改方法的可见性
- 创建一个扩展模拟 class 的本地(内部)class,然后模拟这个本地 class。由于 class 是本地的,因此您可以看到该方法。
John B 是对的,这是因为您要测试的方法受到保护,这不是 Mockito 的问题。
在他列出的选项之上的另一个选项是使用反射来访问该方法。这将使您避免更改正在测试的方法,并避免更改用于编写测试的模式以及存储这些测试的位置。对于一些不允许我更改现有代码库的测试,我不得不自己执行此操作,其中包含大量需要进行单元测试的私有方法。
这些 link 解释了 Reflection 以及如何很好地使用它,所以我会 link 给他们而不是复制:
- What is reflection and whit is it useful
- How to test a class that has private methods, fields, or inner classes
响应 John B 的回答中的选项 3 代码示例的请求:
public class MyClass {
protected String protectedMethod() {
return "Can't touch this";
}
public String publicMethod() {
return protectedMethod();
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
class MyClassMock extends MyClass {
@Override
public String protectedMethod() {
return "You can see me now!";
}
}
@Mock
MyClassMock myClass = mock(MyClassMock.class);
@Test
public void myClassPublicMethodTest() {
when(myClass.publicMethod()).thenCallRealMethod();
when(myClass.protectedMethod()).thenReturn("jk!");
}
}
WhiteBox.invokeMethod() 可以派上用场。
您可以使用 Spring 的 ReflectionTestUtils 来按原样使用您的 class 而无需更改它只是为了测试或将其包装在另一个 class.
public class MyService {
protected JSONObject myProtectedMethod(final String param1, final String param2) {
return new JSONObject();
}
public JSONObject myPublicMethod(final String param1) {
return new JSONObject();
}
}
然后在测试
@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
@Mock
private MyService myService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(myService.myPublicMethod(anyString())).thenReturn(mock(JSONObject.class));
when(ReflectionTestUtils.invokeMethod(myService, "myProtectedMethod", anyString(), anyString())).thenReturn(mock(JSONObject.class));
}
}
public class Test extend TargetClass{
@Override
protected Object method(...) {
return [ValueYouWant];
}
}
在Spring中,你可以像这样设置高优先级:
@TestConfiguration
public class Config {
@Profile({"..."})
@Bean("...")
@Primary // <------ high-priority
public TargetClass TargetClass(){
return new TargetClass() {
@Override
protected WPayResponse validate(...) {
return null;
}
};
}
}
覆盖origin bean也是一样
使用 doReturn() and Junit5's ReflectionSupport.
,像下面这样的东西对我有用
[注意:我在 Mockito 3.12.4 上测试]
ReflectionSupport.invokeMethod(
mymock.getClass()
// .getSuperclass() // Uncomment this, if the protected method defined in the parent class.
.getDeclaredMethod("myMethod", String.class, String.class),
doReturn(myData).when(mymock),
param1,
param2);
我正在使用 Mockito 1.9.5。我如何模拟从受保护方法返回的内容?我有这个受保护的方法…
protected JSONObject myMethod(final String param1, final String param2)
{
…
}
但是,当我尝试在 JUnit 中执行此操作时:
final MyService mymock = Mockito.mock(MyService.class, Mockito.CALLS_REAL_METHODS);
final String pararm1 = “param1”;
Mockito.doReturn(myData).when(mymock).myMethod(param1, param2);
在最后一行,我收到编译错误“方法‘myMethod’不可见。”如何使用 Mockito 模拟受保护的方法?如果这是答案,我愿意升级我的版本。
这不是 Mockito 的问题,而是普通旧 java 的问题。从你调用方法的地方,你没有可见性。这就是为什么它是编译时问题而不是 运行 时问题。
几个选项:
- 在与模拟的包相同的包中声明您的测试 class
- 如果可以,请更改方法的可见性
- 创建一个扩展模拟 class 的本地(内部)class,然后模拟这个本地 class。由于 class 是本地的,因此您可以看到该方法。
John B 是对的,这是因为您要测试的方法受到保护,这不是 Mockito 的问题。
在他列出的选项之上的另一个选项是使用反射来访问该方法。这将使您避免更改正在测试的方法,并避免更改用于编写测试的模式以及存储这些测试的位置。对于一些不允许我更改现有代码库的测试,我不得不自己执行此操作,其中包含大量需要进行单元测试的私有方法。
这些 link 解释了 Reflection 以及如何很好地使用它,所以我会 link 给他们而不是复制:
- What is reflection and whit is it useful
- How to test a class that has private methods, fields, or inner classes
响应 John B 的回答中的选项 3 代码示例的请求:
public class MyClass {
protected String protectedMethod() {
return "Can't touch this";
}
public String publicMethod() {
return protectedMethod();
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
class MyClassMock extends MyClass {
@Override
public String protectedMethod() {
return "You can see me now!";
}
}
@Mock
MyClassMock myClass = mock(MyClassMock.class);
@Test
public void myClassPublicMethodTest() {
when(myClass.publicMethod()).thenCallRealMethod();
when(myClass.protectedMethod()).thenReturn("jk!");
}
}
WhiteBox.invokeMethod() 可以派上用场。
您可以使用 Spring 的 ReflectionTestUtils 来按原样使用您的 class 而无需更改它只是为了测试或将其包装在另一个 class.
public class MyService {
protected JSONObject myProtectedMethod(final String param1, final String param2) {
return new JSONObject();
}
public JSONObject myPublicMethod(final String param1) {
return new JSONObject();
}
}
然后在测试
@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
@Mock
private MyService myService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(myService.myPublicMethod(anyString())).thenReturn(mock(JSONObject.class));
when(ReflectionTestUtils.invokeMethod(myService, "myProtectedMethod", anyString(), anyString())).thenReturn(mock(JSONObject.class));
}
}
public class Test extend TargetClass{
@Override
protected Object method(...) {
return [ValueYouWant];
}
}
在Spring中,你可以像这样设置高优先级:
@TestConfiguration
public class Config {
@Profile({"..."})
@Bean("...")
@Primary // <------ high-priority
public TargetClass TargetClass(){
return new TargetClass() {
@Override
protected WPayResponse validate(...) {
return null;
}
};
}
}
覆盖origin bean也是一样
使用 doReturn() and Junit5's ReflectionSupport.
,像下面这样的东西对我有用[注意:我在 Mockito 3.12.4 上测试]
ReflectionSupport.invokeMethod(
mymock.getClass()
// .getSuperclass() // Uncomment this, if the protected method defined in the parent class.
.getDeclaredMethod("myMethod", String.class, String.class),
doReturn(myData).when(mymock),
param1,
param2);