如何使用 Mockito 模拟受保护的方法?
Posted
技术标签:
【中文标题】如何使用 Mockito 模拟受保护的方法?【英文标题】:How do I use Mockito to mock a protected method? 【发布时间】:2016-03-09 15:26:43 【问题描述】:我使用的是 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’不可见。”如果这是答案,我愿意升级我的版本。
【问题讨论】:
【参考方案1】:使用doReturn() 和Junit5 的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);
【讨论】:
如果我没看错,这是在调用模拟上的方法。您必须记住的是,mock 通常不是被测类,而是被测类中的依赖项。因此,通常在处理模拟时,您不想调用该方法,而是在被测方法调用模拟时引起所需的行为。也就是说,这样的问题很奇怪,因为如果上述情况属实,为什么要模拟受保护的方法而不是可调用的公共方法。 除非受保护的方法在被测类的超类中,在这种情况下你可以考虑模拟被测类。但同样,您不会在测试类中调用受保护的方法,而是在被测方法中调用。但是,如果我不了解您的解决方案是否有效,请告诉我。 在我的情况下(我正在使用这种技术),我有一个带有受保护方法的超类,我正在测试子类,并且我想模拟对超类方法的调用。在浏览了许多 SO 问题和其他博客之后,我推断出了这种技术。有了这个,我的答案与问题***.com/q/34226209/2367394 更相关(我也在那里发布了答案)。尽管相同的技术可以用于类用户测试,即使受保护/私有方法是在同一个类中定义的,也可以通过监视类来实现。因此,我也在这里发布了答案。 如上所述,我知道对于在被测类中定义的受保护方法,它们应该是直接可模拟的,因为我们通常在定义被测类的同一包中定义测试类。 很有趣,我很高兴被指向 ReflectionSupport。也就是说,在恕我直言的情况下,模仿正在测试的班级仍然感觉像是“测试气味”。我还觉得这段代码太晦涩难懂,以至于 2 年后另一个开发人员会不知道正在做什么。我觉得建议的其他解决方案更具可读性/可维护性:扩展类并覆盖受保护的方法。【参考方案2】:public class Test extend TargetClass
@Override
protected Object method(...)
return [ValueYouWant];
在 Spring 中,您可以像这样将其设置为 high-high-priority:
@TestConfiguration
public class Config
@Profile("...")
@Bean("...")
@Primary // <------ high-priority
public TargetClass TargetClass()
return new TargetClass()
@Override
protected WPayResponse validate(...)
return null;
;
覆盖origin bean也是一样。
【讨论】:
【参考方案3】:响应 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!");
【讨论】:
@Override
似乎不是强制性的?另外,spy
不是比mock
更合适吗?
我只能使用 Mockito 和 Java 反射来模拟受保护的方法,而无需覆盖任何类/方法。在此处添加答案:***.com/a/69214093/2367394【参考方案4】:
您可以使用 Spring 的 ReflectionTestUtils 来按原样使用您的类,而无需仅仅为了测试或将其包装在另一个类中而对其进行更改。
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));
【讨论】:
你确定这行得通吗?我得到了同样的错误 @tk_ 这会有帮助吗? github.com/hoomb/testdemo 以上答案对我不起作用。但是我只能使用 Mockito 的 doReturn() 方法和 Java 反射来模拟受保护的方法。在此处添加答案:***.com/a/69214093/2367394【参考方案5】:WhiteBox.invokeMethod() 可以很方便。
【讨论】:
【参考方案6】:John B 是对的,这是因为您尝试测试的方法是受保护的,这不是 Mockito 的问题。
他列出的另一个选项是使用反射来访问该方法。这将允许您避免更改正在测试的方法,并避免更改用于编写测试的模式以及存储这些测试的位置。对于一些不允许我更改现有代码库的测试,我不得不自己执行此操作,其中包括需要进行单元测试的大量私有方法。
这些链接很好地解释了反射以及如何使用它,所以我将链接到它们而不是复制:
What is reflection and whit is it useful How to test a class that has private methods, fields, or inner classes【讨论】:
关于您发布的链接,我没有在反射如何与 Mockito 相关联之间建立联系。您能否发布一些示例代码来展示如何使用 Mockito 模拟受保护的方法? 他没有调用受保护的方法(反射会有所帮助),而是试图模拟受保护的方法(它不是)。【参考方案7】:这不是 Mockito 的问题,而是普通的旧 java。从您调用该方法的位置,您没有可见性。这就是为什么它是编译时问题而不是运行时问题。
几个选项:
在与模拟类相同的包中声明您的测试 如果可以的话,更改方法的可见性 创建一个扩展模拟类的本地(内部)类,然后模拟这个本地类。由于该类是本地的,因此您可以看到该方法。【讨论】:
我选择了选项 #3(创建一个具有覆盖受保护方法的公共方法的类,它只调用 super.method()) 当包名相同但源代码在主文件夹中并且测试类在测试文件夹中时,方法 1 似乎不起作用 我只能使用 Mockito 和 Java 反射来模拟受保护的方法。在此处添加答案:***.com/a/69214093/2367394以上是关于如何使用 Mockito 模拟受保护的方法?的主要内容,如果未能解决你的问题,请参考以下文章