如何使用Mockito模拟Supplier<UUID>类型的Spring Bean?
问题:Mockito无法模拟Supplier类型的Spring Bean(Cucumber测试场景)
原Bean定义
@Bean("idGenerator") public Supplier<UUID> idGenerator() { return () -> UUID.randomUUID(); }
问题现象
在SpringBootTest类中使用@MockBean尝试模拟上述Bean时触发错误,提示无法模拟该类(疑似JVM限制)。后续补充栈信息后确认:标准单元测试场景下可正常运行,问题仅出现在cucumber-java测试中。
临时方案(未被认可)
曾尝试在@TestConfiguration中新增字段,允许外部指定Supplier的返回值,但该方案不够优雅,未得到认可。
补充的栈信息(已翻译)
Before All/After All 执行失败 io.cucumber.core.exception.CompositeCucumberException: 共出现15个异常,详情见下方栈跟踪 at io.cucumber.core.runtime.RethrowingThrowableCollector.getThrowable(RethrowingThrowableCollector.java:57) at io.cucumber.core.runtime.CucumberExecutionContext.getThrowable(CucumberExecutionContext.java:102) at io.cucumber.core.runtime.CucumberExecutionContext.finishTestRun(CucumberExecutionContext.java:97) at io.cucumber.core.runtime.Runtime.execute(Runtime.java:96) at io.cucumber.core.runtime.Runtime.run(Runtime.java:87) at io.cucumber.core.cli.Main.run(Main.java:87) at io.cucumber.core.cli.Main.main(Main.java:30) 被抑制异常: java.lang.IllegalStateException: 加载ApplicationContext失败 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124) at io.cucumber.spring.TestContextAdaptor.<init>(TestContextAdaptor.java:32) at io.cucumber.spring.SpringFactory.start(SpringFactory.java:120) at io.cucumber.core.runner.Runner.buildBackendWorlds(Runner.java:134) [...] 根因: org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为[...]的Bean失败: 构造器参数5存在不满足的依赖; 嵌套异常为 org.mockito.exceptions.base.MockitoException: 无法模拟/监视类 BackendApplicationConfiguration$$Lambda$1713/0x00000008018fd980 Mockito无法模拟/监视的原因: - VM不支持修改指定类型 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
解决方案
方案1:修改原Bean定义(推荐)
将Lambda表达式替换为匿名内部类,避免生成JVM无法修改的Lambda动态类,让Mockito可以正常模拟:
@Bean("idGenerator") public Supplier<UUID> idGenerator() { return new Supplier<UUID>() { @Override public UUID get() { return UUID.randomUUID(); } }; }
修改后,@MockBean即可正常工作:
@SpringBootTest public class YourCucumberSteps { @MockBean(name = "idGenerator") private Supplier<UUID> mockIdGenerator; // 测试步骤中设置预期 @Given("生成指定UUID") public void setupMockUuid() { UUID fixedUuid = UUID.fromString("d290f1ee-6c54-4b01-90e6-d701748f0851"); Mockito.when(mockIdGenerator.get()).thenReturn(fixedUuid); } }
方案2:通过TestConfiguration替换Bean(不修改原代码)
如果无法修改原Bean定义,可通过自定义测试配置替换原Bean,并管理mock对象:
@TestConfiguration public class IdGeneratorTestConfig { private final Supplier<UUID> mockSupplier = Mockito.mock(Supplier.class); @Bean("idGenerator") @Primary // 确保优先使用该Bean替换原Bean public Supplier<UUID> idGenerator() { return mockSupplier; } public Supplier<UUID> getMockSupplier() { return mockSupplier; } }
在Cucumber步骤类中注入配置类,获取mock对象设置预期:
@SpringBootTest(classes = {YourApplication.class, IdGeneratorTestConfig.class}) public class YourCucumberSteps { @Autowired private IdGeneratorTestConfig testConfig; @Given("mock UUID生成器返回 {string}") public void mockUuidGenerator(String uuidStr) { UUID mockUuid = UUID.fromString(uuidStr); Mockito.when(testConfig.getMockSupplier().get()).thenReturn(mockUuid); } }
原因分析
栈信息显示Mockito尝试模拟的是一个Lambda动态生成类(BackendApplicationConfiguration$$Lambda$1713/0x00000008018fd980),JVM对这类动态生成的Lambda类有修改限制,Mockito默认无法模拟。而Cucumber的Spring集成在加载上下文时,直接将原Bean的Lambda实例作为替换目标,而非Supplier接口本身,导致模拟失败;标准单元测试场景下Spring的处理逻辑不同,因此可以正常运行。
内容的提问来源于stack exchange,提问作者Mr. Gung
相关产品推荐
相关产品推荐

