使用mockito-kotlin测试Spring Batch StepScope处理器异常问题
使用Mockito-Kotlin测试Spring Batch StepScope组件时的存根冲突问题
问题描述
我在使用Mockito-Kotlin测试Spring Batch的StepScope处理器时遇到异常:testA执行完成后运行testB,原本期望myService.foo()返回"SomeValue",结果却抛出了IllegalArgumentException("myException")。排查后发现:
- 堆栈信息指向testB的
given语句行 - 所有测试用例共用同一个
myServiceMock实例 - 测试非StepScope的普通组件时无此问题
- 将BDD风格的
given().willReturn()存根改为doReturn().when(mock).myMethod()后问题解决
想问这是Bug还是预期情况?
测试代码示例
目标处理器代码
// Processor to test @StepScope @Component class MyProcessor( private val myService: MyService ) { fun process(item: Item) { // doSomething myService.foo(arg1, arg2) } }
测试类代码
// test @SpringBootTest @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) class MyProcessorTest( private val myProcessor: MyProcessor, @MockBean private val myService: MyService ) { @Test fun testA() { given { myService.foo(any(), any()) } willThrow { IllegalArgumentException("myException") } myProcessor.process(Item(...)) } @Test fun testB() { given { myService.foo(any(), any()) } willReturn { "SomeValue" } myProcessor.process(Item(...)) } }
问题分析与结论
这是预期情况,核心原因和Spring Batch的StepScope特性、Mockito的存根执行逻辑有关:
- StepScope的代理特性:StepScope组件会被Spring包装为代理对象,调用
process方法时,代理逻辑会触发目标方法执行,这个过程会干扰Mockito对方法调用的拦截时机。 - BDD风格
given的执行逻辑:Mockito-Kotlin的given { ... }语法糖会先执行一次目标方法调用(被Mockito拦截返回默认值),再完成存根设置。结合StepScope代理的特殊逻辑,testA中设置的willThrow存根会在testB执行given { myService.foo(...) }时被触发,导致抛出异常,无法完成新的存根设置。 doReturn的优势:doReturn().when(mock).myMethod()属于"存根优先"的语法,不会先执行目标方法,而是直接定义存根行为,因此不会触发之前设置的异常存根,能正常覆盖旧的存根逻辑。- 测试隔离补充:虽然Spring Boot Test默认会在每个测试方法后重置MockBean,但StepScope代理的特殊生命周期可能导致存根重置不及时。除了改用
doReturn风格,也可以通过@MockBean(clear = MockReset.AFTER_EACH_TEST_METHOD)强制每个测试后重置存根。
内容的提问来源于stack exchange,提问作者chl
相关产品推荐
相关产品推荐

