JUnit5测试中Mockito报‘期望调用但未执行’错误的排查
问题重现
测试databaseIsUsedIfDataIsPresent时抛出Mockito错误:
Wanted but not invoked:
dataService.lookup("0140177396");
-> at StockManagementTests.databaseIsUsedIfDataIsPresent(StockManagementTests.java:54)
Actually, there were zero interactions with this mock.
单依赖测试正常,但双同类型依赖时出现该问题,核心是Mockito的@InjectMocks自动注入顺序与构造函数参数顺序不匹配,导致依赖注入错位。
根本原因
你的StockManager构造函数参数顺序是(databaseService, webService),但测试类中@Mock的声明顺序是先webService、后databaseService:
@Mock private ExternalISBNDataService webService; @Mock private ExternalISBNDataService databaseService;
Mockito在处理构造函数注入时,默认会按@Mock的声明顺序将Mock实例注入到构造函数的参数中:
- 第一个声明的
webServiceMock被注入到构造函数第一个参数databaseService - 第二个声明的
databaseServiceMock被注入到构造函数第二个参数webService
这就导致StockManager实际调用的是webService的Mock(被当成了databaseService),而你verify的是真正的databaseService Mock,自然显示"零交互"。
解决办法
方法1:调整@Mock声明顺序(最简单)
将databaseService的@Mock声明放在webService前面,与构造函数参数顺序一致:
@Mock private ExternalISBNDataService databaseService; // 先声明,对应构造函数第一个参数 @Mock private ExternalISBNDataService webService; // 后声明,对应构造函数第二个参数 @InjectMocks private StockManager stockManager;
方法2:手动注入(最可控)
放弃@InjectMocks,在@BeforeEach中手动创建StockManager实例,明确传入对应Mock:
@BeforeEach public void setUp() { isbn = "0140177396"; // 手动注入,完全避免自动注入的顺序问题 stockManager = new StockManager(databaseService, webService); }
方法3:使用@Named注解(适合复杂场景)
如果需要明确区分同类型依赖,可结合@Named注解(需引入javax.inject或jakarta.inject依赖):
- 修改
StockManager构造函数,给参数添加@Named:
import javax.inject.Named; public StockManager(@Named("databaseService") ExternalISBNDataService databaseService, @Named("webService") ExternalISBNDataService webService) { this.databaseService = databaseService; this.webService = webService; }
- 在测试类中给Mock添加
@Named:
import javax.inject.Named; @Mock @Named("webService") private ExternalISBNDataService webService; @Mock @Named("databaseService") private ExternalISBNDataService databaseService; @InjectMocks private StockManager stockManager;
验证效果
采用以上任一方法后,重新运行测试,databaseIsUsedIfDataIsPresent中的verify(databaseService, times(1)).lookup(isbn)会正确捕获到调用,测试通过。
内容的提问来源于stack exchange,提问作者Haitam-Elgharras

