此篇可以参考:
在项目中我们经常会用到通过http方式和其他系统交互,在salesforce 零基础学习(三十三)通过REST方式访问外部数据以及JAVA通过rest方式访问salesforce这篇讲过http callout方式使用,
简单callout demo如下:
1 public class CalloutClass {
2
3 //default out of time
4 private static final Integer OUT_OF_TIME = 10000;
5 //default method : get
6 private static final String DEFAULT_METHOD_GET = 'GET';
7
8 private static final Integer STATUS_CODE_OK = 200;
9
10 public static String getDataViaHttp(String endPoint,String param) {
11 return getDataViaHttp(endPoint,DEFAULT_METHOD_GET,param);
12 }
13
14 public static String getDataViaHttp(String endPoint,String method,String param) {
15 return getDataViaHttp(endPoint,method,param,OUT_OF_TIME);
16 }
17
18 public static String getDataViaHttp(String endPoint,String method,String param,Integer outOfTime) {
19 HttpRequest req = new HttpRequest();
20 Http h = new Http();
21 req.setMethod(method);
22 req.setHeader('Content-Type', 'application/json');
23 if(param != null) {
24 req.setBody(param);
25 }
26 req.setEndpoint(endPoint);
27 req.setTimeout(outOfTime);
28 HttpResponse res = h.send(req);
29 if(res.getStatusCode() == STATUS_CODE_OK) {
30 return res.getBody();
31 } else {
32 throw new CallOutException('访问失败');
33 }
34 }
35
36 class CallOutException extends Exception {
37
38 }
39 }
有的时候我们需要在batch中调用http接口和其他系统交互进行字段更新等操作,如果在batch中需要用到http callout,需要实现Database.AllowsCallouts接口,demo如下:
1 public with sharing class CalloutBatchClass implements Database.Batchable<sObject>,Database.AllowsCallouts{
2 public Database.QueryLocator start(Database.BatchableContext BC) {
3 String fetchSQL = 'fetch sql';
4 return Database.getQueryLocator(fetchSQL);
5 }
6
7 public void execute(Database.BatchableContext BC, List<sObject> objList) {
8 String endPoint = 'site end point';
9 String responseData = CalloutClass.getDataViaHttp(endPoint,null);
10 for(sObject obj : objList) {
11 //TODO
12 }
13 }
14
15 public void finish(Database.BatchableContext BC) {
16
17 }
18 }
项目中test class是必需的,而且正常要求test class覆盖率超过75%。test class中不允许http callout,我们可以通过实现HttpCalloutMock接口模拟http请求的返回值。通过重写respond方法实现
不同的http请求所返回的不同的response状态和body内容。
1 @isTest
2 global class MockHttpResponseGenerator implements HttpCalloutMock {
3 global String method;
4
5 global String METHOD1_BODY = '{"foo":"bar"}';
6
7 global String METHOD2_BODY = '{"foo":"bar2"}';
8
9 global MockHttpResponseGenerator() {}
10
11
12 global MockHttpResponseGenerator(String requestMethod) {
13 method = requestMethod;
14 }
15
16 // Implement this interface method
17 global HTTPResponse respond(HTTPRequest req) {
18 // Create a fake response
19 HttpResponse res = new HttpResponse();
20 res.setHeader('Content-Type', 'application/json');
21 String body;
22 if(method == 'method1') {
23 body = METHOD1_BODY;
24 } else if(method == 'method2') {
25 body = METHOD2_BODY;
26 } else if(method == 'methodError') {
27 res.setStatusCode(500);
28 }
29 res.setBody('{"foo":"bar"}');
30 if(res.getStatusCode() != null) {
31 res.setStatusCode(200);
32 }
33 return res;
34 }
35 }
简单的测试CalloutClass的测试类如下:
@isTest
private class CalloutClassTest {
@isTest static void testSuccessCallout() {
Test.startTest();
// Set mock callout class
Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator('method1'));
String endPoint = 'http://api.salesforce.com/foo/bar';
String result = CalloutClass.getDataViaHttp(endPoint,'test param');
String expectedValue = '{"foo":"bar"}';
System.assertEquals(result, expectedValue);
Test.stopTest();
}
}
这只是我们碰到的所谓最理想的情况,有的时候我们往往会碰到这样一种情况:一个方法里面需要调用到多个http callout。比如需要先进行http callout,将返回值作为参数或者oauth setting内容然后继续进行callout,这种情况下使用上述的方式便比较难实现,毕竟上述mock形式仅作为一个http callout的response。这个时候我们要变通一下,看看前面的调用是否是必要的--前后几次调用是否有并列关系,还是仅将前几次调用作为相关参数为最后一次做准备,此种情况下,可以在类中设置相关的静态变量来跳过相关的调用;如果前后几次调用属于并列关系,需要对每一次的response的内容进行相关处理,这种情况下的test class便需要使用multi mock形式。
一.非并列关系:此种方式可以使用变量方式跳过相关的调用
1 public with sharing class CalloutClassUseVariable {
2 public static Boolean skipForTest{get;set;}
3 public STring getResult(String endPoint1,String endPoint2) {
4 String result1 = '';
5 if(skipForTest == null ||skipForTest == false) {
6 result1 = CalloutClass.getDataViaHttp(endPoint1,'');
7 }
8 String result2 = CalloutClass.getDataViaHttp(endPoint2,result1);
9 return result2;
10 }
11 }
相关test class处理
1 @isTest
2 private class CalloutClassUseVariableTest {
3 static testMethod void testMethod1() {
4 Test.startTest();
5 Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator('method1'));
6 String endPoint = 'http://api.salesforce.com/foo/bar';
7 CalloutClassUseVariable.skipForTest = true;
8 String result = CalloutClassUseVariable.getResult('', endPoint);
9 String expectedValue = '{"foo":"bar"}';
10 System.assertEquals(result, expectedValue);
11 Test.stopTest();
12 }
13 }
二.并列关系:此种方式需要使用MultiStaticResourceCalloutMock方式。
salesforce提供MultiStaticResourceCalloutMock接口实现多个callout的test class模拟response请求,可以将response的body值放在txt文档中上传至static resources中,然后test class引用相关静态资源实现模拟多个response返回。
1 public with sharing class CalloutController {
2 public String result{get;set;}
3 public void getResult(String endPoint1,String endPoint2) {
4 String result1 = CalloutClass.getDataViaHttp(endPoint1,'');
5 String result2 = CalloutClass.getDataViaHttp(endPoint2,'');
6 result = result1 + result2;
7 }
8 }
相关test class处理:
1.将需要的相关response body值上传至static resource中;
2.test class编写
1 @isTest
2 private class CalloutClassUseMultiStaticResourceTest {
3 static testMethod void testMethod1() {
4 MultiStaticResourceCalloutMock mock = new MultiStaticResourceCalloutMock();
5 String endPoint1 = 'http://api.salesforce.com/foo/bar';
6 String endPoint2 = 'http://api.salesforce.com/foo/sfdc';
7 mock.setStaticResource(endPoint1, 'Callout_Method1_TestResponse');
8 mock.setStaticResource(endPoint2, 'Callout_Method2_TestResponse');
9 mock.setStatusCode(200);
10 mock.setHeader('Content-Type', 'application/json');
11 Test.setMock(HttpCalloutMock.class, mock);
12 Test.startTest();
13 CalloutController controller = new CalloutController();
14 controller.getResult(endPoint1,endPoint2);
15 String expectedResult = '{"foo":"bar"}{"foo":"bar2"}';
16 system.assertEquals(expectedResult,controller.result);
17 Test.stopTest();
18 }
19 }
总结:callout test class编写可以主要看方法中对于callout执行次数以及形式,如果仅是单次请求或者非并列形式,推荐使用httpcalloutMock方式,简单粗暴,而且自己造数据,不用上传静态资源,即使在其他环境下也可以正常跑,如果进行了多次请求,并且请求之间需要有并行操作那就只能使用multi callout 形式,使用此种方式记得在移到其他平台以前将静态资源上传。如果篇中有错误地方欢迎指正,有问题欢迎留言。