如何为泛型方法编写单元测试?无需创建特定泛型类型
如何在不创建特定泛型类型的情况下测试泛型Service类?
先贴出待测试的泛型Service代码:
public class Service<T, E> { private final Class<T> domainClass; private final Class<E> entityClass; private final Repository<E> repository; private final AnotherService conversionService; // 补充构造方法(原代码未展示,测试需依赖) public Service(Class<T> domainClass, Class<E> entityClass, Repository<E> repository, AnotherService conversionService) { this.domainClass = domainClass; this.entityClass = entityClass; this.repository = repository; this.conversionService = conversionService; } public ResponseDto deleteAllThenImportAll(Collection<T> data) { Collection<E> entities = conversionService.convert(data, domainClass, entityClass); ResponseEntity entity = repository.deleteAllThenImportAll(entities); return conversionService.convert(entity); } } // 补充测试所需的依赖类定义 interface Repository<E> { ResponseEntity deleteAllThenImportAll(Collection<E> entities); } class AnotherService { <T, E> Collection<E> convert(Collection<T> data, Class<T> domainClass, Class<E> entityClass) { return null; } ResponseDto convert(ResponseEntity entity) { return null; } } class ResponseEntity {} class ResponseDto {}
测试思路
Java泛型采用类型擦除机制,运行时不会保留泛型参数的具体类型信息。因此我们完全可以用通配符(<?, ?>)或者Object作为泛型参数创建Service实例,配合Mockito模拟所有依赖,只验证方法的调用逻辑是否符合预期,不需要关心T和E的具体类型。
具体测试实现(JUnit 5 + Mockito)
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Collections; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ServiceTest { @Mock private Repository<Object> mockRepository; @Mock private AnotherService mockConversionService; // 直接用Object作为泛型参数创建实例,传入任意Class对象即可 @InjectMocks private Service<Object, Object> service = new Service<>(Object.class, Object.class, mockRepository, mockConversionService); @Test void deleteAllThenImportAll_ShouldFollowCallFlow() { // 准备测试数据 Collection<Object> testData = Collections.singletonList(new Object()); Collection<Object> mockEntities = Collections.singletonList(new Object()); ResponseEntity mockResponseEntity = new ResponseEntity(); ResponseDto expectedDto = new ResponseDto(); // 模拟依赖方法的返回值 when(mockConversionService.convert(testData, Object.class, Object.class)).thenReturn(mockEntities); when(mockRepository.deleteAllThenImportAll(mockEntities)).thenReturn(mockResponseEntity); when(mockConversionService.convert(mockResponseEntity)).thenReturn(expectedDto); // 执行测试方法 ResponseDto actualDto = service.deleteAllThenImportAll(testData); // 验证调用逻辑和返回值 verify(mockConversionService).convert(testData, Object.class, Object.class); verify(mockRepository).deleteAllThenImportAll(mockEntities); verify(mockConversionService).convert(mockResponseEntity); assert actualDto == expectedDto; } }
关键要点
- 利用泛型擦除特性,不需要定义具体的T、E实现类,直接用
Object占位即可 - 所有依赖(Repository、AnotherService)都用Mockito模拟,避免依赖真实的类型转换或数据库操作
- 测试核心是验证方法调用顺序和参数传递是否符合预期,而非真实的业务逻辑实现
- 如果需要验证类型参数的正确性,可以通过ArgumentMatchers的
eq方法匹配传入的Class对象
内容的提问来源于stack exchange,提问作者Black Rabbit
相关产品推荐
相关产品推荐

