Mockito模拟LDAPConnectionPool时总是调用父类真实方法问题
Mockito模拟LDAPConnectionPool.search返回null的问题解决
问题场景
尝试用Mockito模拟LDAPConnectionPool的search方法生成测试数据,但实际调用时始终返回null,断点验证发现真实方法被执行。排查数小时后怀疑和抽象父类有关,但无法修改这些类,调试难度较高。
依赖环境
使用的Mockito相关Maven依赖:
<artifactId>mockito-junit-jupiter</artifactId> <version>5.4.0</version> <artifactId>mockito-core</artifactId> <version>5.4.0</version> <artifactId>mockito-inline</artifactId> <version>5.2.0</version>
调用日志
方法调用日志显示传入的第一个参数为null,返回结果为null:
############ Logging method invocation #2 on mock/spy ######## ldapConnectionPool.search( null, SUB, "(mail=a@b.c)" ); invoked: -> at package.Class.method(Class.java:228) has returned: "null"
相关类定义
- 被模拟类:
package com.unboundid.ldap.sdk; public final class LDAPConnectionPool extends AbstractConnectionPool
- 父类真实方法:
public final SearchResult search(final String baseDN, final SearchScope scope, final String filter, final String... attributes) throws LDAPSearchException { return search(new SearchRequest(baseDN, scope, parseFilter(filter), attributes)); }
初始测试代码
最初的测试代码中,使用带类型限制的匹配器设置模拟返回:
@ExtendWith(SpringExtension.class) class ClassTest { @MockBean private LdapConnectionPoolProvider ldapConnectionPoolProvider; private LDAPConnectionPool ldapConnectionPool= mock(LDAPConnectionPool.class, withSettings().verboseLogging());; private AutoCloseable closeable; @BeforeEach void beforeTest() { closeable= MockitoAnnotations.openMocks(this); } @AfterEach void afterTest() throws Exception { closeable.close(); } @Test void methodSuccess() throws LDAPSearchException { SearchResult res = new SearchResult(1243, ResultCode.SUCCESS, "", "", null, searchEntries, null, 0, 0, null); // 此处匹配器设置导致模拟失效 doReturn(res).when(ldapConnectionPool).search(any(String.class), any(SearchScope.class), any(String.class)); SearchResult result = ldapConnectionPool.search(getBaseDN(), SearchScope.SUB, "(mail=" + getMail() + ")"); } }
问题复现与解决方法
通过复现案例验证,发现问题出在参数匹配器的使用上:
复现测试代码
@Slf4j public class Tester { private LDAPConnectionPool ldapConnectionPool = mock(LDAPConnectionPool.class, withSettings().verboseLogging()); private AutoCloseable closeable; @Mock private LdapAIOConnection ldapAIOConnection; @BeforeEach void init() { log.info("@BeforeEach - executes before each test method in this class"); } @BeforeEach void beforeTest() { closeable = MockitoAnnotations.openMocks(this); doReturn(ldapConnectionPool).when(ldapAIOConnection).ldapConnectionPool(); } @AfterEach void afterTest() throws Exception { closeable.close(); } @Test void testIt() throws LDAPSearchException { List<SearchResultEntry> searchEntries = new ArrayList<>(); String GUID = "??"; String accControlCode = "512"; String lastPassSet = "1234"; Collection<Attribute> attrs = new HashSet<>(Arrays.asList(new Attribute("objectGUID", GUID), new Attribute("pwdLastSet", lastPassSet), new Attribute("userAccountControl", accControlCode))); searchEntries.add(new SearchResultEntry("", attrs, new Control("12"))); SearchResult res = new SearchResult(1243, ResultCode.SUCCESS, "a.a.c", "a/a/a", null, searchEntries, null, 1, 1, null); // 写法1:模拟失效,返回null // doReturn(res).when(ldapConnectionPool).search(any(String.class), any(SearchScope.class), any(String.class)); // 写法2:模拟成功,返回预期结果 doReturn(res).when(ldapConnectionPool).search(any(), any(SearchScope.class), any(String.class)); SearchResult result = ldapConnectionPool.search(null, SearchScope.SUB, ""); Assertions.assertEquals(1, result.getEntryCount()); } }
解决方法
将第一个参数的匹配器从any(String.class)改为无类型限制的any(),即可让Mockito正确匹配方法调用并返回模拟结果。
原因分析:当传入的参数为null时,any(String.class)无法匹配null值(因为null不属于任何类型的实例),而无类型的any()可以匹配包括null在内的任意参数,因此能正确触发模拟逻辑。
内容的提问来源于stack exchange,提问作者Martin P.
相关产品推荐
相关产品推荐

