如何用Mockito验证方法参数:匹配指定ID且仅校验复杂对象非空字段
问题
需要调整Mockito的verify方法实现以下校验:
- 第一个参数与预期值精确匹配
- 第二个参数为
List<Profile>类型,其中Profile实例可能存在部分字段为null,仅验证已赋值的非空字段,忽略未赋值的null字段 - 确认被Mock的
valid方法调用时传入的userId正确
当前相关代码如下:
现有校验方法
public void verifyProfileOnce(UserId userId, Profile profile) { verify(profileClientMockBean, atLeastOnce()).valid(eq(userId), argThat(capturedList -> capturedList.stream().filter(capturedProfile -> profile.equals(capturedProfile)).count() == 1)); }
被Mock方法签名
void valid(UserId userId, List<Profile> profile);
测试调用示例
profileMock.verifyProfileOnce(userId.toString(),builder().name(TEST).email("test@test").address("qwerty").comment("First test").build());
解决方案
完全可以实现需求,核心是自定义字段匹配逻辑,忽略Profile中的null字段,仅对比已赋值的属性。以下是两种可行实现方式:
方式一:自定义ArgumentMatcher(推荐,可复用)
创建专门的匹配器类,封装字段对比逻辑:
import org.mockito.ArgumentMatcher; public class ProfilePartialMatcher implements ArgumentMatcher<Profile> { private final Profile expected; public ProfilePartialMatcher(Profile expected) { this.expected = expected; } @Override public boolean matches(Profile actual) { // 仅对比预期对象中非null的字段 if (expected.getName() != null && !expected.getName().equals(actual.getName())) { return false; } if (expected.getEmail() != null && !expected.getEmail().equals(actual.getEmail())) { return false; } if (expected.getAddress() != null && !expected.getAddress().equals(actual.getAddress())) { return false; } if (expected.getComment() != null && !expected.getComment().equals(actual.getComment())) { return false; } // 可根据Profile的其他字段扩展对比逻辑 return true; } }
修改校验方法,结合自定义匹配器验证列表中的Profile:
public void verifyProfileOnce(UserId userId, Profile expectedProfile) { verify(profileClientMockBean, atLeastOnce()).valid( eq(userId), argThat(capturedList -> // 验证列表中存在至少一个符合条件的Profile capturedList.stream().anyMatch(actual -> new ProfilePartialMatcher(expectedProfile).matches(actual)) // 若需要恰好一个匹配项,替换为: // capturedList.stream().filter(actual -> new ProfilePartialMatcher(expectedProfile).matches(actual)).count() == 1 ) ); }
方式二:Lambda内直接实现逻辑(快速验证)
如果不需要复用匹配逻辑,可直接在argThat的Lambda中编写对比代码:
public void verifyProfileOnce(UserId userId, Profile expectedProfile) { verify(profileClientMockBean, atLeastOnce()).valid( eq(userId), argThat(capturedList -> capturedList.stream().anyMatch(actual -> { boolean isMatch = true; if (expectedProfile.getName() != null) { isMatch &= expectedProfile.getName().equals(actual.getName()); } if (expectedProfile.getEmail() != null) { isMatch &= expectedProfile.getEmail().equals(actual.getEmail()); } if (expectedProfile.getAddress() != null) { isMatch &= expectedProfile.getAddress().equals(actual.getAddress()); } if (expectedProfile.getComment() != null) { isMatch &= expectedProfile.getComment().equals(actual.getComment()); } return isMatch; }) ) ); }
注意事项
- 确保
UserId类正确实现了equals和hashCode方法,否则eq(userId)的匹配会失效 - 若
Profile字段较多,优先选择方式一,代码更易维护和扩展 - 根据测试需求,将
anyMatch替换为count() == 1可验证列表中恰好存在一个匹配的Profile
内容的提问来源于stack exchange,提问作者epb
相关产品推荐
相关产品推荐

