为何Mockito类型匹配器匹配不相关对象?如何实现Mock行为依赖参数类型?
一、为什么类型匹配器会匹配不相关的对象?
你遇到的这个问题通常有几个常见原因,我帮你拆解下:
匹配器的范围理解偏差
Matchers.any(A.class)(现在更推荐用ArgumentMatchers.any(A.class))的作用是匹配任何非null的、可以被转换为A类型的对象——包括A的实现类、子类实例。如果你的测试参数同时实现了A和B接口(比如一个类ImplAB implements A,B),那这个参数会同时满足两个匹配器的条件,Mockito会返回最后一次stubbing的结果(也就是"B received"),这时候你可能会误以为它错误匹配了不相关的B类型。误用了无参的
any()匹配器
如果你不小心写成了Matchers.any()而不是any(A.class),这个匹配器会匹配所有Object类型,后面的any(B.class)会完全覆盖前面的stubbing,导致所有调用都返回"B received",看起来就像匹配了不相关的对象。参数类型实际不符合预期
比如你以为传的是A类型实例,但实际代码中传的是Object或者其他不相关类型,这时候两个匹配器都不生效,Mockito会返回默认的null,你可能误以为这是匹配了某个错误的分支。
二、正确实现Mock对象按参数类型返回不同结果
你的代码思路是对的,只需要调整几个细节就能达到预期效果:
首先,推荐使用ArgumentMatchers.isA(Class)替代any(Class)——isA()会严格匹配类型(不包含null),而any(Class)会匹配null,根据你的需求选合适的即可。然后确保测试时传入的是对应类型的实例,而不是父类Object。
补全后的完整测试代码如下:
import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class MockitoTest { public interface ToMock { String accept(Object object); } interface A {} interface B {} // 定义A和B的实现类,用于测试传参 static class ImplA implements A {} static class ImplB implements B {} @Test public void doAnswer() { ToMock mock = Mockito.mock(ToMock.class); // 针对A类型参数设置返回值 Mockito.doReturn("A received") .when(mock) .accept(ArgumentMatchers.isA(A.class)); // 针对B类型参数设置返回值 Mockito.doReturn("B received") .when(mock) .accept(ArgumentMatchers.isA(B.class)); // 验证A类型参数的返回 Assert.assertEquals("A received", mock.accept(new ImplA())); // 验证B类型参数的返回 Assert.assertEquals("B received", mock.accept(new ImplB())); // 验证非A/B类型参数返回默认值null Assert.assertNull(mock.accept(new Object())); } }
额外注意点
- 如果你的业务场景需要处理null参数,可以单独加一个stubbing:
Mockito.doReturn("Null received").when(mock).accept(ArgumentMatchers.isNull()); - 当多个匹配器存在覆盖关系时(比如A继承B),Mockito会优先选择最具体的匹配器(比如
isA(A.class)比isA(B.class)更具体),这时候stubbing的顺序不影响结果;但如果匹配器范围完全重叠,最后一次stubbing会覆盖前面的。
内容的提问来源于stack exchange,提问作者Basilevs

