zoukankan      html  css  js  c++  java
  • salesforce 零基础学习(六十八)http callout test class写法

    此篇可以参考:

    https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_testing_httpcalloutmock.htm

    https://developer.salesforce.com/trailhead/force_com_dev_intermediate/apex_integration_services/apex_integration_rest_callouts

    在项目中我们经常会用到通过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 形式,使用此种方式记得在移到其他平台以前将静态资源上传。如果篇中有错误地方欢迎指正,有问题欢迎留言。

  • 相关阅读:
    Dex-Net笔记
    python笔记
    linux和anaconda
    JPA和Hibernate的关系
    eclipse利用JPA,根据数据库表生成Java中的实体类
    eclipse中利用hibernate插件,根据数据库表反向生成Javabean
    eclipse中git常用操作说明
    oracle常用语法
    threadlocal
    spring用到的一些注解小札
  • 原文地址:https://www.cnblogs.com/zero-zyq/p/6574863.html
Copyright © 2011-2022 走看看