2023年9月7日
おはようございます!こんにちは!こんばんは!
駆け出し新入社員の「す」でございます!
早速ですがみなさま、 Salesforceで開発を行っているときにこんな状況に陥ったことはございませんか???
「テストクラスが動かない!環境で動作確認はできるのに!どうして!」
私は先日プライベートで開発しているときにこの状況に陥りました…
ずばり原因は Apexの中で外部APIを使用しているため!
はい!
では今回は
なぜSalesforce上で外部APIを使うとテストが実行できないのか?
そして外部APIを使ったテストを行うためのMockとはなんぞや?
実際にサンプル使ってMockを使ってみよう
このような流れで書いていきます!
ではまいりましょう!
そもそもなぜSalesforce環境で外部APIを使ったテストができないのか?
その答えはずばり そういう仕様だから!
「はい?その詳細を教えてくれよ」
はい。すみません。
SalesforceのSandbox環境やDE(Developer Edition)環境では許可なく外部への通信ができません。
環境上で使う際は環境設定として、その外部サイトへのアクセスを許可してあげる必要があります。
その許可があって初めて外部APIをSalesforce環境で使えるようになります。
しかしテストにはその許可が及びません。
したがって外部APIを使ったApexのテストは失敗してしまいます。
▲ テストの失敗
外部APIを使ったApexの単体テストを行うときに使うMockとは何者なのか?
一言でいうと「仮想的な接続先」です。
「急に固い言葉出てきた!」
という方のたたのためにかみ砕いて説説明していきますのでご安心ください。
まずAPIをわかりやすく例えると「会話」です。
Apexさん「私、〇〇な条件の情報がほしいな」
接続先くん「はい。これが君が欲しがってた情報だよ」
この会話を成り立たせている言葉がAPIと呼ばれるものだと思ってください。
ふつうはこれで完結ですが、テストの時は違います
テストのときはApexさんの親であるSalesforce環境さんが
「接続先くんと会話することは許さない!」
こう言っているわけです。
これではApexさんは必要な情報を得ることができません。
なので仕方なくApexさんは
「仮にこんな返事が来たら」
「おそらくこんな情報が返ってくる予定だ」
と仮説を立てて情報をもらったふりをします。
Apexさん「私、〇〇な条件の情報がほしいな」
に対してApexさん自身が
「接続先くんからはこんな情報がもらえるだろうな」
と考えて勝手に物事を進めるわけです。
これがMockというものです
実際のAPIの動作をApexさんと接続先くんの「会話」とするならば
Mockを使ったAPIテストの動作はApexさんの「独り言」になります
このようにすることでApexさんはテストでも接続先からデータをもらえたと仮定して、その先の動作をテストすることが可能になります。
これを実現させるのがMockクラスと呼ばれるものです。
なんとなくのイメージがつかめましたでしょうか?
兎にも角にも使ってみるのがいちばん!
実際にサンプルをふまえながら理解を深めましょう!
今回はサンプルとして、APIを呼び出してリスポンスとして返ってきたステータスコードを出力するというコードのテストを行っていきます。
// sample
public with sharing class ApiSample {
public static String ApiSample() {
String AccessToken = 'your access token';
String endpointUrl = 'this is endpoint';
String requestBody = endpointUrlMedia + 'your request';
// リクエスト作成
HttpRequest request = new HttpRequest();
request.setHeader('Authorization', 'Bearer ' + AccessToken);
request.setEndpoint(requestBody);
request.setMethod('POST');
String result;
try {
Http http = new Http();
HttpResponse response = http.send(request);
result = String.valueOf(response.getStatusCode());
} catch (Exception e) {
result = e.getMessage();
System.debug('Exception: ' + e.getMessage());
}
return result;
}
}
こちらを実行すると以下のような結果が戻ってくるサンプルです。
▲ 成功した場合の出力
これに対してAPIを考慮しない普通のテストクラスを書いて動かしてみます。
// APIを考慮しないテストクラス
@isTest
public class ApiSampleTest {
@isTest
static void ApiSampleTest() {
String result;
Test.startTest();
result = ApiSample.ApiSample();
Test.stopTest();
System.assertEquals('200', result);
}
すると以下のようなエラーが出てテストが失敗してしまいます。
▲ テストを実行して出てくるエラー
この状態を避けるためにMockクラスを書いていきます。
// Mockクラス サンプル
@isTest
public class ApiSampleMock implements HttpCalloutMock{
public HttpResponse respond(HttpRequest req){
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
res.setStatusCode(200);
res.setBody('{"id":"17972272688534358"}');
return res;
}
}
こちらがApexの独り言であるMockクラスです
引数としてHTTPRequest型のreqを与えることでHttpResponse型のresを返す作りになっています。
もちろん独り言なのでresの中身はAPIの公式ドキュメントやレスポンスサンプルから「こんな値が返ってくるだろう」という調査を経て自分で作成します。
こちらをテストクラスに盛り込むとこうなります。
// APIを考慮してMockクラスを盛り込んだテストクラス
@isTest
public class ApiSampleTest {
@isTest
static void ApiSampleMockTest() {
String result;
Test.startTest();
Test.setMock(HttpCalloutMock.class, new ApiSampleMock());
result = ApiSample.ApiSample();
Test.stopTest();
System.assertEquals('200', result);
}
}
このなかのTest.setMock()の部分で作成したMockクラスを使用しています。
こちらのテストクラスを実行するとこのようになります。
▲ テスト実行結果
はい。API部分はMockで作成した結果が返ってきて、テストが成功していることが分かります。
独り言成功です!
今回のブログはいかがだったでしょうか?
個人的にはMockを「独り言」に例えられた自分に拍手を送りたいですね笑
「テストが動かねえ!なんで!」
こんなことを嘆いていた自分と同じ状況になっている方の参考になれば幸いです。
ではまた次回のブログでお会いしましょう!さようなら~