如何在Spring Data JPA中用Mockito对QuerydslBinderCustomizer做单元测试?
问题:如何优化Spring Data JPA QuerydslBinderCustomizer的单元测试?
我正尝试为Spring Data JPA仓库中的QuerydslBinderCustomizer方法编写单元测试,以下是我的实现代码:
@Override default void customize(QuerydslBindings bindings, QGenericClass root) { bindings.bind(String.class) .first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase); }
我的目标是验证customize方法是否正确为String字段配置了containsIgnoreCase条件的绑定。我已使用Mockito编写了如下测试代码:
@Test void testCustomize() { QuerydslBindings bindings = mock(QuerydslBindings.class); QGenericClass qGenericClass = mock(QGenericClass.class); genericClassRepository.customize(bindings, qGenericClass); ArgumentCaptor<SingleValueBinding> captor = ArgumentCaptor.forClass(SingleValueBinding.class); verify(bindings).bind(eq(String.class), captor.capture()); SingleValueBinding<StringPath, String> binding = captor.getValue(); assertNotNull(binding); StringPath stringPath = mock(StringPath.class); when(stringPath.containsIgnoreCase(any())).thenReturn(mock(StringExpression.class)); binding.bind(stringPath, () -> "testValue"); verify(stringPath).containsIgnoreCase("testValue"); }
请问是否有更优的测试写法,或是我遗漏了什么关键要点?
优化建议与要点检查
你的测试思路没问题,下面是几个优化方向和需要注意的关键细节:
1. 提升ArgumentCaptor的类型安全性
当前代码里ArgumentCaptor<SingleValueBinding>用了原始类型,建议指定泛型参数,避免类型转换隐患:
ArgumentCaptor<SingleValueBinding<StringPath, String>> captor = ArgumentCaptor.forClass((Class<SingleValueBinding<StringPath, String>>) (Class<?>) SingleValueBinding.class);
2. 简化绑定逻辑的验证
不需要mockStringExpression的返回值,我们只关心containsIgnoreCase是否被正确调用,直接验证即可:
// 移除这行冗余代码:when(stringPath.containsIgnoreCase(any())).thenReturn(mock(StringExpression.class)); binding.bind(stringPath, () -> "testValue"); verify(stringPath).containsIgnoreCase(eq("testValue"));
3. 验证未使用的参数行为
你的实现里没用到root参数,可以添加验证确认代码没有对它做任何操作,避免后续变更时遗漏:
verifyNoInteractions(qGenericClass);
4. 用@Mock注解简化测试代码
借助Mockito的@Mock注解减少手动mock的重复代码,让测试更整洁:
@Mock private QuerydslBindings bindings; @Mock private QGenericClass qGenericClass; @Mock private StringPath stringPath; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test void testCustomize() { genericClassRepository.customize(bindings, qGenericClass); ArgumentCaptor<SingleValueBinding<StringPath, String>> captor = ArgumentCaptor.forClass((Class<SingleValueBinding<StringPath, String>>) (Class<?>) SingleValueBinding.class); verify(bindings).bind(eq(String.class), captor.capture()); SingleValueBinding<StringPath, String> binding = captor.getValue(); assertNotNull(binding); binding.bind(stringPath, () -> "testValue"); verify(stringPath).containsIgnoreCase("testValue"); verifyNoInteractions(qGenericClass); }
5. 补充验证first()方法的调用
当前测试只捕获了传给bind()的绑定逻辑,但如果代码不小心改成bindings.bind(String.class).all(...),测试不会发现问题。可以通过mockStringBinding来补充验证:
// 在测试中添加: StringBinding stringBinding = mock(StringBinding.class); when(bindings.bind(eq(String.class))).thenReturn(stringBinding); genericClassRepository.customize(bindings, qGenericClass); ArgumentCaptor<SingleValueBinding<StringPath, String>> captor = ArgumentCaptor.forClass((Class<SingleValueBinding<StringPath, String>>) (Class<?>) SingleValueBinding.class); verify(stringBinding).first(captor.capture());
这样就能确保first()方法被正确调用,而不是其他多值绑定方法。
内容的提问来源于stack exchange,提问作者Lorenzo Vicino
相关产品推荐
相关产品推荐

