此篇可以參考:
在項目中我們經常會用到通過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 形式,使用此種方式記得在移到其他平台以前將靜態資源上傳。如果篇中有錯誤地方歡迎指正,有問題歡迎留言。