【Salesforce】外部サービスと連携するApexクラスをテストする方法（Mockを利用します）

# 1.はじめに
どうも、ARIの名古屋支社に勤務している愛知県民です♪
(/・ω・)/

Apexクラスを本番環境に作成する際はSandboxにてテストが必要ですが、
**Sandboxは外部と通信できない仕様のため、
外部サービス（例えばAWSのLamda）と連携するApexクラスの場合は、
特殊なテストクラスの作成が必要になります。**

そこで今回は**外部サービスと連携するApexクラスのテスト方法について**記事にしたいと思います。
当時、自分自身が沼ったので、同様の事象で悩んでいる人の参考になれば幸いです♪
(*^^)v

:::note info
厳密には、SalesforceのSandboxからはHTTPコールアウトができないという仕様で、
普通にテストしようとすると以下の様なエラーが出ます。
「XXXとして定義されたメソッドは、WEBサービスコールアウトをサポートしません。」
:::

# 2.用語の説明
## 2.1.Sandboxとは
Sandboxとは本番環境にアップロードするApexクラスやApexトリガを作成・テストするための環境のことです。

## 2.2.Apexとは
Apexとは、JavaベースのSalesforce独自のプログラム言語です。

Salesforce内のオブジェクトの検索や作成をするメソッド等が提供されており、
それらを組み合わせて処理を記述することができます。

Apexクラスとは、オブジェクト指向におけるクラスと同様のものとなります。

# 3.前提条件
以下に前提条件を示します。

* Sandboxが作成済であること

# 4.概要
Sandboxから外部へは通信ができないため、外部サービスと連携するApexクラスをテストするためには、
**仮想的な接続先であるMockを作成し、テストを実施する時にはMockに応答させます。**

# 5.具体例
今回は、AWSのAPIゲートウェイに接続するApexクラスを例にして説明したいと思います。
また、APIはREST APIとします。

Apexクラスの作成方法については以下の記事をご確認ください。

https://qiita.com/Aichi_Lover/items/06573a82d9af2f982b7f

## 5.1.Apexクラス
Apexクラス「CallLambda」は以下となります。

```
global class CallLambda {
    public String callLambda(String want) {
     HttpRequest req = new HttpRequest();
     req.setEndpoint('★APIゲートウェイのURL★');
     req.setMethod('GET');
       
     //JSON形式に変換する
     Http http = new Http();
     HTTPResponse res = http.send(req);
     String resJson = JSON.serialize(res.getBody());
     System.debug('リクエスト結果：' + resJson);
     
     if(want == 'all'){
         return resJson;
     }
     
     //Map型にしてから1つ抽出する
     Map<String, Object> resMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody()); 
     String element = (String)resMap.get(want);
     System.debug('抽出結果：' + element);
     return element;
     }
}
```

Apexクラスには特殊な設定は不要です。
処理は**APIゲートウェイにリクエストを投げ、引数の文字列をキーとし、
レスポンスから値を抽出する**という流れとなっています。

## 5.2.Mock
Apexクラス「CallLambda」のテストクラスを作る際に必要となる、Mockは以下となります。
MockもApexクラスとして作成します。

```
global class CalloutMock implements HttpCalloutMock {
    global HTTPResponse respond(HTTPRequest req) {
        // Optionally, only send a mock response for a specific endpoint
        // and method.
        System.assertEquals('★APIゲートウェイのURL★', req.getEndpoint());
        System.assertEquals('GET', req.getMethod());
        
        // Create a fake response
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"example":"test"}');
        res.setStatusCode(200);
        return res;
   }
}
```

処理は**接続先とメソッドを確認したのち、レスポンスを返す**という流れとなります。

今回はREST APIのため、「implements HttpCalloutMock」を使用しており、
「System.assertEquals(A,B)」はAとBが同じ値でない場合に、エラーを返します。

## 5.3.テストクラス
Apexクラス「CallLambda」のテストクラスは以下となります。
テストクラスもApexクラスとして作成します。

```
@isTest
private class CallLambdaTest {

    @isTest static void callLambda() {
        Test.setMock(HttpCalloutMock.class, new CalloutMock());
        
        CallLambda A= new CallLambda();
        String result = A.callLambda('example');
    }
}
```
ポイントとなるのは
```
Test.setMock(HttpCalloutMock.class, new CalloutMock());
```

の行で、ここで前節で作成したMockを使用する設定をしています。
その後、メソッドが呼び出されるとMockが応答します。

# 6.動作確認
Apexクラスをテストするとログの末尾は以下のようになります。

|![テスト結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372071/426de514-9fa4-6065-6b9c-b96dad67f6b3.png)|
|:--|

**上の赤枠にて、リクエスト結果としてMockで設定した{"example":"test"}が返ってきたこと、
下の赤枠にて、「example」をキーとして値「test」が抽出できていること**
が確認できます。

# 7.おわりに
ここまで読んで下さり、ありがとうございます！！！
(^^)

外部と接続するApexクラスを作るときは、
仮想的な接続先であるMockも作るというのが勉強になりました。

実際に外部と通信するのは本番環境にアップロードしてからなので、
そこでエラーになるとSandboxにて修正、テスト、本番環境にアップロード
という一連の流れが必要になるのが少々大変でした・・・

さて、次回はCTIフローからApexクラスのメソッドを呼び出し、
呼び出したApexクラスのメソッドからさらにLambdaを呼び出す方法について記載したいと思います！
(:3_ヽ)_