我正在进行一个普通的 Spring Boot 集成测试,它与
@Autowired
和 @MockBean
配合得很好,一切都是真实的,只是对外界的其余模板进行了嘲笑
@SpringBootTest
@DirtiesContext
@ExtendWith(ConsoleReporterExtension.class)
@ActiveProfiles("it")
@SpringJUnitConfig(Application.class)
@AutoConfigureMockMvc
@AutoConfigureWireMock(port = 0)
@ContextConfiguration(initializers = {TestcontainersInitializer.class}) // for some docker DB
...
@Autowired
private SomeRepository someRepository;
然后我需要模拟持久化过程中的一个问题,并决定监视 bean:
//@Autowired
@SpyBean
private SomeRepository someRepository;
其余的测试仍然工作正常,因此间谍 bean 在应用程序内正确传播,并且因为它没有被存根,所以它的行为与自动连接的一样。 但是当我在
save
期间对其进行存根以抛出一次性异常时
when(someRepository.save(any()))
.thenThrow(new OptimisticLockingFailureException("some concurrency issue"))
.thenCallRealMethod();
它在存根上失败了,就好像有人将
null
传递给 save 方法一样,但事实并非如此,特别是我们还没有到达那里,而是它发生在存根定义期间。我缺少什么? (Spring Boot v2.7.10)
java.lang.IllegalArgumentException: Aggregate instance must not be null!
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.springframework.data.jdbc.core.JdbcAggregateTemplate.save(JdbcAggregateTemplate.java:153)
at org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.save(SimpleJdbcRepository.java:78)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:530)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:286)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:640)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:81)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at jdk.proxy2/jdk.proxy2.$Proxy223.save(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.mockito.internal.util.reflection.ReflectionMemberAccessor.invoke(ReflectionMemberAccessor.java:48)
at org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations.answer(ForwardsInvocations.java:49)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:110)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:161)
at SomeRepository$MockitoMock$cs2Dymza.save(Unknown Source)
at SomeRepository$MockitoMock$cs2Dymza$$FastClassBySpringCGLIB$$b0e4e380.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
at SomeRepository$MockitoMock$cs2Dymza$$EnhancerBySpringCGLIB$$6546aaba.save(<generated>)
顺便说一句,该存储库只是一个 Crud 存储库(甚至尝试了一种 hack 来显式重写保存方法,但这显然没有改变任何东西)
@Repository
public interface SomeRepository extends CrudRepository<SomeEntity, Long> {
//<S extends SomeEntity> S save(S entity);
它在存根上失败了,就好像有人将
传递给 save 方法一样,但事实并非如此,特别是我们还没有到达那里,而是它发生在存根定义期间。null
事实上,确实如此。您正在调用该方法并在此行中传递
null
:
when(someRepository.save(any()))
ArgumentMatchers#any
的实现:
public static <T> T any() {
reportMatcher(Any.ANY);
return null; // <- returns null
}
Java 是急切求值的,并且总是在将方法的返回值传递给另一个方法之前调用方法。
when(obj.method())
必须先调用method()
,然后才能调用when
。 Mockito 无法改变这个事实。
doThrow
/doCallRealMethod
形式:
doThrow(new OptimisticLockingFailureException("some concurrency issue"))
.doCallRealMethod()
.when(someRepository)
.save(any());
更多信息: