如何单元测试由自定义元数据控制的Apex HTTP调用模拟逻辑?
解决Apex单元测试中自定义元数据类型(MDT)依赖环境的问题
针对你遇到的单元测试依赖环境中MDT记录的问题,这里有几个实用的解决方案:
方案1:测试类中动态创建专属MDT记录
Apex测试支持通过Metadata API在测试上下文里临时创建MDT记录,测试结束后这些记录会自动清理,不会影响生产环境。这种方式完全隔离了测试与现有环境的依赖。
示例代码:
@IsTest private class HttpCalloutTest { @IsTest static void testMockResponseFromMDT() { // 构建要创建的MDT记录 My_Custom_MDT__mdt testMDT = new My_Custom_MDT__mdt( DeveloperName = 'Test_Response', Label = 'Test Response', Static_JSON__c = '{"status":"success","data":"test"}' ); // 准备部署请求 Metadata.DeployContainer container = new Metadata.DeployContainer(); container.addMetadata(testMDT); // 执行部署并等待完成 Id deployId = Metadata.Operations.enqueueDeployment(container, null); Test.waitForDeploy(deployId); // 验证部署结果 Metadata.DeployResult result = Metadata.Operations.checkDeployStatus(deployId, true); System.assert(result.isSuccess(), 'MDT部署失败: ' + result.getErrorMessage()); // 开启模拟开关,执行业务逻辑测试 My_Custom_Setting__c setting = My_Custom_Setting__c.getOrgDefaults(); setting.Enable_Mock__c = true; update setting; // 调用待测试的HTTP调用方法 HttpResponse response = MyHttpCalloutService.makeCallout(); // 断言响应是否符合MDT中的静态JSON System.assertEquals('{"status":"success","data":"test"}', response.getBody()); } }
方案2:抽象MDT读取逻辑,测试时注入模拟实现
通过面向接口编程,把读取MDT的逻辑和业务逻辑解耦,测试时直接使用模拟的响应数据,完全绕开MDT的依赖。
步骤:
- 定义响应提供接口
public interface HttpResponseProvider { String getMockResponse(); }
- 生产环境实现(读取MDT)
public class MetadataResponseProvider implements HttpResponseProvider { public String getMockResponse() { My_Custom_MDT__mdt mdtRecord = My_Custom_MDT__mdt.getInstance('Default_Response'); return mdtRecord?.Static_JSON__c; } }
- 测试用模拟实现
@IsTest private class MockResponseProvider implements HttpResponseProvider { public String getMockResponse() { return '{"status":"success","data":"mock_test"}'; } }
- 业务逻辑类依赖注入
public class MyHttpCalloutService { private static HttpResponseProvider provider; // 生产环境默认使用MDT实现 static { provider = new MetadataResponseProvider(); } // 测试时调用此方法替换为模拟实现 @TestVisible private static void setProvider(HttpResponseProvider customProvider) { provider = customProvider; } public static HttpResponse makeCallout() { My_Custom_Setting__c setting = My_Custom_Setting__c.getOrgDefaults(); if(setting.Enable_Mock__c) { HttpResponse mockResponse = new HttpResponse(); mockResponse.setBody(provider.getMockResponse()); mockResponse.setStatusCode(200); return mockResponse; } else { // 真实HTTP调用逻辑 HttpRequest req = new HttpRequest(); // ... 设置请求参数 Http http = new Http(); return http.send(req); } } }
- 测试类使用模拟实现
@IsTest private class HttpCalloutTest { @IsTest static void testMockResponse() { // 替换为模拟提供者 MyHttpCalloutService.setProvider(new MockResponseProvider()); // 开启模拟开关 My_Custom_Setting__c setting = My_Custom_Setting__c.getOrgDefaults(); setting.Enable_Mock__c = true; update setting; HttpResponse response = MyHttpCalloutService.makeCallout(); System.assertEquals('{"status":"success","data":"mock_test"}', response.getBody()); } }
方案3:使用Test.loadData导入静态资源中的测试数据
将测试用的MDT数据预定义为JSON格式的静态资源,测试时通过Test.loadData导入,无需手动创建MDT记录。
步骤:
- 创建静态资源(比如命名为
MDT_Test_Data),内容为:
[ { "DeveloperName": "Test_Response", "Label": "Test Response", "Static_JSON__c": "{\"status\":\"success\",\"data\":\"load_data_test\"}" } ]
- 测试类中加载数据
@IsTest private class HttpCalloutTest { @IsTest static void testMockResponseFromLoadedMDT() { // 从静态资源加载MDT测试数据 List<SObject> mdtRecords = Test.loadData(My_Custom_MDT__mdt.SObjectType, 'MDT_Test_Data'); System.assert(!mdtRecords.isEmpty(), '未加载到MDT测试数据'); // 开启模拟开关并执行测试 My_Custom_Setting__c setting = My_Custom_Setting__c.getOrgDefaults(); setting.Enable_Mock__c = true; update setting; HttpResponse response = MyHttpCalloutService.makeCallout(); System.assertEquals('{"status":"success","data":"load_data_test"}', response.getBody()); } }
这三个方案中,方案2的解耦程度最高,完全避免了对MDT环境的依赖;方案1和3则更贴近生产逻辑,适合需要验证MDT读取流程的场景。
内容的提问来源于stack exchange,提问作者Xi Xiao
相关产品推荐
相关产品推荐

