单元测试在IntelliJ IDEA中运行成功,Maven命令执行失败
我编写了一个简单的单元测试,代码如下:
@Test public void testHandleInterruptedException() { InterruptedException exception = Mockito.mock(InterruptedException.class); Assertions.assertNotNull(YammerService.handleInterruptedException(exception, Boolean.TRUE)); Assertions.assertNotNull(YammerService.handleInterruptedException(exception, Boolean.FALSE)); }
奇怪的是,这个测试在IntelliJ IDEA中运行通过,但使用Maven构建命令执行时失败。
对应的handleInterruptedException方法实现很简单:
public static KendraConnectorException handleInterruptedException( InterruptedException e, boolean isContinuable) { return isContinuable ? new ContinuableInternalServerError( "Current thread has been interrupted", e) : new InternalServerError("Please try again later", e); }
我不确定Maven执行失败的原因,补充完整的YammerServiceTest测试类代码,可能问题出在测试类中:
package com.amazonaws.kendra.connector.yammer.client; import com.amazonaws.kendra.connector.sdk.exception.BadRequestException; import com.amazonaws.kendra.connector.sdk.exception.ContinuableInternalServerError; import com.amazonaws.kendra.connector.yammer.configuration.YammerConfiguration; import com.amazonaws.kendra.connector.yammer.rest.client.api.MyselfApi; import com.amazonaws.kendra.connector.yammer.service.UserService; import feign.FeignException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; import java.util.Objects; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) public class YammerServiceTest { private MockedStatic<UserService> userService = Mockito.mockStatic(UserService.class); private YammerConfiguration yammerConfiguration = Mockito.mock(YammerConfiguration.class); private MyselfApi myselfApi = Mockito.mock(MyselfApi.class); @BeforeEach public void setUp() { userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenReturn(myselfApi); Mockito.when(myselfApi.getCurrentUser()).thenReturn(DataProvider.getUser()); Mockito.when(yammerConfiguration.getHostUrl()).thenReturn(DataProvider.URL); Mockito.when(yammerConfiguration.getAuthType()).thenReturn(DataProvider.OAUTH2); Mockito.when(yammerConfiguration.getClientId()).thenReturn(DataProvider.CLIENT_ID); Mockito.when(yammerConfiguration.getClientSecret()).thenReturn(DataProvider.CLIENT_SECRET); Mockito.when(yammerConfiguration.getUserName()).thenReturn(DataProvider.USER_NAME); Mockito.when(yammerConfiguration.getPassword()).thenReturn(DataProvider.PASSWORD); Mockito.when(yammerConfiguration.getScope()).thenReturn(DataProvider.SCOPE); } @AfterEach void tearDown() { if (!Objects.isNull(userService)) { userService.close(); } userService = null; } @Test public void testReTestConnection() { YammerConfiguration yammerConfiguration = Mockito.mock(YammerConfiguration.class); YammerService yammerService = new YammerService(); Assertions.assertTrue(yammerService.reTestConnection(yammerConfiguration)); } @Test public void testTestConnection() { YammerService yammerService = new YammerService(); Assertions.assertTrue(yammerService.testConnection(yammerConfiguration)); } @Test public void testReTestConnectionWhenFeignExceptionIsThrown() { YammerConfiguration configuration = Mockito.mock(YammerConfiguration.class); FeignException exception = Mockito.mock(FeignException.class); Mockito.when(configuration.getHostUrl()).thenThrow(exception); Mockito.when(exception.status()).thenReturn(429); Assertions.assertFalse(YammerService.reTestConnection(configuration)); } @Test public void testReTestConnectionWhenFeignExceptionIsThrown_1() { YammerConfiguration configuration = Mockito.mock(YammerConfiguration.class); FeignException exception = Mockito.mock(FeignException.class); Mockito.when(configuration.getHostUrl()).thenThrow(exception); Mockito.when(exception.status()).thenReturn(430); Assertions.assertFalse(YammerService.reTestConnection(configuration)); } @Test public void testTestConnectionWhenFeignExceptionIsThrown() { YammerConfiguration configuration = Mockito.mock(YammerConfiguration.class); FeignException exception = Mockito.mock(FeignException.class); Mockito.when(configuration.getHostUrl()).thenReturn(DataProvider.URL); Mockito.when(configuration.getAuthType()).thenThrow(exception); Mockito.when(exception.status()).thenReturn(430); Mockito.when(exception.getCause()).thenReturn(exception); Assertions.assertThrows(BadRequestException.class, () -> YammerService.testConnection(configuration)); } //@Test public void testTestConnectionWithThrotlingWhenFeignExceptionIsThrown() { YammerConfiguration configuration = Mockito.mock(YammerConfiguration.class); FeignException exception = Mockito.mock(FeignException.class); Mockito.when(configuration.getHostUrl()).thenReturn(DataProvider.URL); Mockito.when(configuration.getAuthType()).thenThrow(exception); Mockito.when(exception.status()).thenReturn(429); Mockito.when(exception.getCause()).thenReturn(exception); Assertions.assertThrows(BadRequestException.class, () -> YammerService.testConnection(configuration)); } @Test public void testHandleInterruptedException() { InterruptedException exception = new InterruptedException("InterruptedException"); Assertions.assertNotNull(YammerService.handleInterruptedException(exception, Boolean.TRUE)); Assertions.assertNotNull(YammerService.handleInterruptedException(exception, Boolean.FALSE)); } @Test public void testTestConnectionWhenNotAuth2() { YammerService yammerService = new YammerService(); List<Integer> waitTimeList = Arrays.asList(1); FeignException feignException = Mockito.mock(FeignException.class); Mockito.when(feignException.status()).thenReturn(429); Mockito.when(feignException.getCause()).thenReturn(new UnknownHostException()); Mockito.when(yammerConfiguration.getAuthType()).thenReturn(DataProvider.OAUTH); userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenThrow(feignException); Assertions.assertThrows(BadRequestException.class, () -> yammerService.testConnection(yammerConfiguration)); } @Test public void testTestConnectionFeignExceptionIsThrown() { YammerService yammerService = new YammerService(); List<Integer> waitTimeList = Arrays.asList(1); FeignException feignException = Mockito.mock(FeignException.class); Mockito.when(feignException.status()).thenReturn(429); Mockito.when(feignException.getCause()).thenReturn(new Exception()); Mockito.when(yammerConfiguration.getAuthType()).thenReturn(DataProvider.OAUTH); userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenThrow(feignException); Thread.currentThread().interrupt(); Assertions.assertThrows(ContinuableInternalServerError.class, () -> yammerService.testConnection(yammerConfiguration)); } @Test public void testTestConnectionWhenCauseIsNull() { YammerService yammerService = new YammerService(); List<Integer> waitTimeList = Arrays.asList(1); FeignException feignException = Mockito.mock(FeignException.class); Mockito.when(feignException.status()).thenReturn(429); Mockito.when(feignException.getCause()).thenReturn(null); Mockito.when(yammerConfiguration.getAuthType()).thenReturn(DataProvider.OAUTH); userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenThrow(feignException); Assertions.assertThrows(BadRequestException.class, () -> yammerService.testConnection(yammerConfiguration)); } }
1. 线程中断状态未重置导致后续测试异常
在testTestConnectionFeignExceptionIsThrown方法中,调用了Thread.currentThread().interrupt();但未在测试结束后清除线程的中断状态。Maven默认串行执行测试,若该测试在testHandleInterruptedException之前执行,会导致后续测试线程的中断标志始终为true。部分JUnit内部逻辑或断言操作会检查线程中断状态,进而抛出未预期的异常导致测试失败。
修复方法:在该测试方法中添加finally块,清除线程中断状态:
@Test public void testTestConnectionFeignExceptionIsThrown() { try { YammerService yammerService = new YammerService(); List<Integer> waitTimeList = Arrays.asList(1); FeignException feignException = Mockito.mock(FeignException.class); Mockito.when(feignException.status()).thenReturn(429); Mockito.when(feignException.getCause()).thenReturn(new Exception()); Mockito.when(yammerConfiguration.getAuthType()).thenReturn(DataProvider.OAUTH); userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenThrow(feignException); Thread.currentThread().interrupt(); Assertions.assertThrows(ContinuableInternalServerError.class, () -> yammerService.testConnection(yammerConfiguration)); } finally { // 清除线程中断状态,避免影响后续测试 Thread.interrupted(); } }
2. 静态Mock创建时机不当导致状态冲突
测试类中userService静态Mock是在成员变量初始化时创建的,而JUnit Jupiter每个测试方法都会创建一个新的测试类实例。这会导致多个静态Mock实例重叠,前一个实例的tearDown关闭静态Mock后,后续实例的静态Mock可能出现状态异常,在Maven的测试执行流程中更容易触发问题。
修复方法:将静态Mock的创建移到setUp方法中,确保每个测试方法前都创建全新的静态Mock:
// 修改成员变量声明,去掉初始化逻辑 private MockedStatic<UserService> userService; @BeforeEach public void setUp() { // 在每个测试前创建新的静态Mock userService = Mockito.mockStatic(UserService.class); userService.when(() -> UserService.getMyselfApi(Mockito.any())) .thenReturn(myselfApi); // 其他Mock设置逻辑... }
3. 可选优化:避免Mock InterruptedException
InterruptedException是受检异常,直接实例化而非Mock更符合实际场景,也能避免Mockito对异常类Mock可能带来的潜在问题(比如部分异常方法的默认行为被Mock覆盖)。你已经在修改后的测试代码中改成了new InterruptedException,这部分可以保留。
完成以上修改后,重新执行Maven构建命令,测试应该能正常通过。
内容的提问来源于stack exchange,提问作者Gunwant

