如何结合Mockito与AssertJ的递归比较验证方法调用参数?
问题:结合Mockito与AssertJ递归比较验证方法调用参数
在测试场景中,针对未实现hashCode()/equals()方法的数据对象,AssertJ提供了便捷的递归比较API:
Assertions.assertThat(result) .usingRecursiveComparison() .ignoringFields("id") .isEqualTo(expected)
而验证方法调用情况时,通常使用Mockito的如下写法:
Mockito.verify(mock, times(2)).method(eq(arg1), eq(arg2));
现在希望将两者结合,在Mockito的verify验证中,用AssertJ的递归比较逻辑来匹配方法参数,比如实现类似这样的调用:
Mockito.verify(mock, times(2)).method(usingRecursiveComparison(arg1));
请问是否可以通过Mockito+AssertJ实现该需求,或是有其他支持此类API的工具库?
解决方案
方法一:自定义Mockito ArgumentMatcher封装AssertJ递归比较
Mockito支持自定义ArgumentMatcher,可以直接把AssertJ的递归比较逻辑封装进去,实现复用:
- 编写自定义Matcher类:
import org.assertj.core.api.Assertions; import org.mockito.ArgumentMatcher; public class RecursiveEqualsMatcher<T> implements ArgumentMatcher<T> { private final T expected; private final String[] ignoredFields; private RecursiveEqualsMatcher(T expected, String... ignoredFields) { this.expected = expected; this.ignoredFields = ignoredFields; } // 静态工厂方法,模拟AssertJ的API风格 public static <T> RecursiveEqualsMatcher<T> usingRecursiveComparison(T expected) { return new RecursiveEqualsMatcher<>(expected); } // 支持忽略指定字段的重载方法 public static <T> RecursiveEqualsMatcher<T> usingRecursiveComparison(T expected, String... ignoredFields) { return new RecursiveEqualsMatcher<>(expected, ignoredFields); } @Override public boolean matches(T actual) { try { Assertions.assertThat(actual) .usingRecursiveComparison() .ignoringFields(ignoredFields) .isEqualTo(expected); return true; } catch (AssertionError e) { // 断言失败则返回false,Mockito会判定参数不匹配 return false; } } @Override public String toString() { // 自定义描述,验证失败时能输出清晰的错误信息 return "recursive equals to: " + expected + (ignoredFields.length > 0 ? " (ignoring fields: " + String.join(", ", ignoredFields) + ")" : ""); } }
- 测试中的使用方式:
// 基础递归比较 Mockito.verify(mock, times(2)).method(RecursiveEqualsMatcher.usingRecursiveComparison(arg1)); // 忽略指定字段的递归比较 Mockito.verify(mock, times(2)).method(RecursiveEqualsMatcher.usingRecursiveComparison(arg1, "id", "createTime"));
方法二:使用Mockito的argThat内联实现
如果只是临时场景,不想编写自定义Matcher,可以直接用Mockito的argThat方法内联AssertJ的断言逻辑:
Mockito.verify(mock, times(2)).method(argThat(actual -> { try { Assertions.assertThat(actual) .usingRecursiveComparison() .ignoringFields("id") .isEqualTo(arg1); return true; } catch (AssertionError e) { return false; } }));
这种方式灵活但复用性较差,适合单次使用的场景。
方法三:使用官方扩展库AssertJ-Mockito
AssertJ官方提供了assertj-mockito扩展库,直接支持在Mockito验证中使用AssertJ的所有断言逻辑(包括递归比较),无需自己实现Matcher:
使用示例:
import org.assertj.mockito.api.Assertions; // 验证方法调用次数及参数匹配 Assertions.assertThat(mock).hasBeenCalled().times(2) .withMethodCall(method -> method.targetMethod( // 直接使用AssertJ的递归比较断言参数 Assertions.assertThat(arg1).usingRecursiveComparison().ignoringFields("id") ));
这个库是官方维护的,兼容性和稳定性都有保障。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

