Java新手求助:如何用Mockito框架Mock/Stub JCo库做单元测试
嘿,作为有丰富C/C++经验的开发者,你转Java做SAP JCo的单元测试其实上手会特别快——Mockito的核心思路和你熟悉的Google Mock这类框架是通的,只是语法和Java生态的细节需要适应下。我给你一步步拆解怎么Mock JCo库,不用真实连SAP就能完成单元测试:
第一步:先梳理你要Mock的JCo核心节点
调用BAPI的常规流程是:获取JCoDestination → 从Destination拿JCoRepository → 用Repository获取目标JCoFunction → 设置参数 → 执行Function → 处理返回参数。所以需要Mock的核心类/接口包括:
JCoDestination:模拟SAP服务器连接JCoRepository:模拟BAPI元数据仓库JCoFunction:模拟具体的BAPI函数JCoParameterList:模拟BAPI的输入/输出参数列表
第二步:添加Mockito依赖
首先给你的测试环境加上Mockito和Junit 5的依赖(以Maven为例,Gradle思路类似):
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency>
第三步:封装JCo操作(推荐)
直接测试裸JCo调用会让Mock变得繁琐,建议把SAP交互逻辑封装到自己的服务类里,比如:
import com.sap.conn.jco.*; public class SAPMaterialService { public String getMaterialDescription(String destName, String materialId) throws JCoException { // 获取连接 JCoDestination destination = JCoDestinationManager.getDestination(destName); // 获取BAPI函数 JCoFunction bapi = destination.getRepository().getFunction("BAPI_MATERIAL_GET_DETAIL"); if (bapi == null) { throw new RuntimeException("BAPI不存在: BAPI_MATERIAL_GET_DETAIL"); } // 设置输入参数 JCoParameterList importParams = bapi.getImportParameterList(); importParams.setValue("MATERIAL", materialId); // 执行BAPI bapi.execute(destination); // 获取输出参数 JCoParameterList exportParams = bapi.getExportParameterList(); return exportParams.getString("MAKTX"); } }
第四步:编写Mock单元测试
现在可以用Mockito模拟所有JCo依赖,不用真实连接SAP:
import com.sap.conn.jco.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class SAPMaterialServiceTest { // 用@Mock注解自动创建Mock对象,类似C++里的NiceMock @Mock private JCoDestination mockDest; @Mock private JCoRepository mockRepo; @Mock private JCoFunction mockBapi; @Mock private JCoParameterList mockImportParams; @Mock private JCoParameterList mockExportParams; @Test void getMaterialDescription_Success() throws JCoException { // Mock静态方法JCoDestinationManager.getDestination(Mockito 3.4+支持) try (MockedStatic<JCoDestinationManager> mockedStatic = mockStatic(JCoDestinationManager.class)) { // 定义静态方法的返回值 mockedStatic.when(() -> JCoDestinationManager.getDestination("TEST_SAP")) .thenReturn(mockDest); // 定义Mock对象的调用逻辑 when(mockDest.getRepository()).thenReturn(mockRepo); when(mockRepo.getFunction("BAPI_MATERIAL_GET_DETAIL")).thenReturn(mockBapi); when(mockBapi.getImportParameterList()).thenReturn(mockImportParams); when(mockBapi.getExportParameterList()).thenReturn(mockExportParams); // 模拟输出参数返回预期值 when(mockExportParams.getString("MAKTX")).thenReturn("测试物料"); // 执行测试方法 SAPMaterialService service = new SAPMaterialService(); String result = service.getMaterialDescription("TEST_SAP", "MAT001"); // 验证结果和调用行为 assertEquals("测试物料", result); // 验证输入参数是否被正确设置 verify(mockImportParams, times(1)).setValue("MATERIAL", "MAT001"); // 验证BAPI是否被执行 verify(mockBapi, times(1)).execute(mockDest); } } @Test void getMaterialDescription_BapiNotFound() throws JCoException { try (MockedStatic<JCoDestinationManager> mockedStatic = mockStatic(JCoDestinationManager.class)) { mockedStatic.when(() -> JCoDestinationManager.getDestination("TEST_SAP")) .thenReturn(mockDest); when(mockDest.getRepository()).thenReturn(mockRepo); // 模拟找不到BAPI的场景 when(mockRepo.getFunction("BAPI_MATERIAL_GET_DETAIL")).thenReturn(null); SAPMaterialService service = new SAPMaterialService(); RuntimeException exception = assertThrows(RuntimeException.class, () -> service.getMaterialDescription("TEST_SAP", "MAT001")); assertEquals("BAPI不存在: BAPI_MATERIAL_GET_DETAIL", exception.getMessage()); } } }
给C/C++开发者的额外提示
- Mockito的
@Mock类似你熟悉的Google MockNiceMock,默认不会因为意外调用报错 when(...).thenReturn(...)对应GMock的EXPECT_CALL(...).WillOnce(Return(...)),用来定义Mock对象的返回行为- 静态Mock的
MockedStatic用try-with-resources管理,类似C++的RAII,避免静态Mock的副作用残留 - 如果JCo的类是final的,Mockito默认无法Mock,不过JCo核心类大多是接口或非final类,一般不用操心这个
内容的提问来源于stack exchange,提问作者dkrypt
相关产品推荐
相关产品推荐

