HttpCalloutMock と StubProvider のお試し

#1.概要
HttpCalloutMock と StubProvider は同じ目的を果たします。
Apex ランタイム人事前に用意した擬似応答（偽物）を返却する。

**HttpCalloutMockはクラス単位です。**
**StubProviderはメソッド単位です。**


#2.[HttpCalloutMock](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_interface_httpcalloutmock.htm)の使用パターン
0.HttpCalloutMockインターフェースを実装するクラス作成
1.テストデータ作成
2.[Test.setMock](https://developer.salesforce.com/docs/atlas.ja-jp.218.0.apexcode.meta/apexcode/apex_methods_system_test.htm#apex_System_Test_createStub)
3.Test.startTest
4.テストメソッド起動
5.Test.stopTest
6.結果検証

#3.[StubProvider](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_interface_System_StubProvider.htm)の使用パータン
0.StubProvider インターフェースを実装するクラス作成
1.テストデータ作成
2.[Test.createStub](https://developer.salesforce.com/docs/atlas.ja-jp.218.0.apexcode.meta/apexcode/apex_methods_system_test.htm#apex_System_Test_createStub)
　**※返却値は重要です。該当返却値しかモックできません。**
3.Test.startTest
4.テストメソッド起動
5.Test.stopTest
6.結果検証

#4.お試し例
```java

public class AnimalsCallouts {
    public HttpResponse makeGetCallout() {
        system.debug('makeGetCallout処理');
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        return response;
    }
    public HttpResponse makePostCallout() {
        system.debug('makePostCallout処理');
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        request.setBody('{"name":"mighty moose"}');
        HttpResponse response = http.send(request);
        return response;
    }
}
```
```java

@isTest
private class AnimalsCalloutsTest {
    @isTest static  void testGetCallout() {
        Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

        Test.startTest();
        AnimalsCallouts animalsCallout = new AnimalsCallouts();
        HttpResponse response = animalsCallout.makeGetCallout();
        System.debug('testGetCallout:' + response.getBody());
        Test.stopTest();

        String actualValue = response.getBody();
        String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
        System.assertEquals(actualValue, expectedValue);
    }
    
    @isTest static void testPostCallout() {
        Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

        Test.startTest();
        AnimalsCallouts animalsCallout = new AnimalsCallouts();
        HttpResponse response = animalsCallout.makePostCallout();
        System.debug('testGetCallout:' + response.getBody());
        Test.stopTest();

        String actualValue = response.getBody();
        String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
        System.assertEquals(actualValue, expectedValue);
    }
    
    @isTest static  void testGetCalloutStub() {
        AnimalsCallouts animalsCallout = (AnimalsCallouts)Test.createStub(AnimalsCallouts.class, new AnimalsCalloutStubProvider());
        
        Test.startTest();
        HttpResponse response = animalsCallout.makeGetCallout();
        System.debug('testGetCalloutStub:' + response.getBody());
        Test.stopTest();
        
        String actualValue = response.getBody();
        String expectedValue = '{"animals": ["000", "000", "000", "000", "000"]}';
        System.assertEquals(actualValue, expectedValue);
    }
    
    @isTest static void testPostCalloutStub() {
        AnimalsCallouts animalsCallout = (AnimalsCallouts)Test.createStub(AnimalsCallouts.class, new AnimalsCalloutStubProvider());
        
        Test.startTest();
        HttpResponse response = animalsCallout.makePostCallout();
        System.debug('testPostCalloutStub:' + response.getBody());
        Test.stopTest();
        
        String actualValue = response.getBody();
        String expectedValue = '{"animals": ["111", "111", "111", "111", "111"]}';
        System.assertEquals(actualValue, expectedValue);
    }

    // HttpCalloutMockインターフェースを実装するクラス作成
    private class AnimalsHttpCalloutMock implements HttpCalloutMock {
        public HTTPResponse respond(HTTPRequest request) {
            HttpResponse response = new HttpResponse();
            response.setHeader('Content-Type', 'application/json');
            response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}');
            response.setStatusCode(200);
            return response; 
        }
    }
    
    // StubProvider インターフェースを実装するクラス作成
    private class AnimalsCalloutStubProvider implements System.StubProvider {
        public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames,  List<Object> listOfArgs) {
            if(stubbedMethodName == 'makeGetCallout') {
                HttpResponse response = new HttpResponse();
                response.setHeader('Content-Type', 'application/json');
                response.setBody('{"animals": ["000", "000", "000", "000", "000"]}');
                response.setStatusCode(200);
                return response;
                
            } else if (stubbedMethodName == 'makePostCallout') {
                HttpResponse response = new HttpResponse();
                response.setHeader('Content-Type', 'application/json');
                response.setBody('{"animals": ["111", "111", "111", "111", "111"]}');
                response.setStatusCode(200);
                return response;
            }
            return null;
        }
    }
}
```

#5.Apex スタブ API の制限事項
Apex スタブ API を操作するときは次の点に注意してください。

- 模造されるオブジェクトは、Test.createStub() メソッドへのコールと同じ名前空間にある必要があります。ただし、StubProvider インターフェースの実装は別の名前空間でも構いません。
- 次の Apex 要素は模造できません。
- 静的メソッド (future メソッドを含む)
    - 非公開メソッド
    - プロパティ (getter および setter)
    - トリガ
    - 内部クラス
    - システム型
    - Batchable インターフェースを実装するクラス
    - 非公開コンストラクタのみを持つクラス
- 戻り値の型またはパラメータの型としてイテレータは使用できません。

#6.参照
[Use Mocks and Stub Objects](https://trailhead.salesforce.com/ja/content/learn/modules/unit-testing-on-the-lightning-platform/mock-stub-objects)
[Apex REST コールアウト](https://trailhead.salesforce.com/ja/content/learn/modules/apex_integration_services/apex_integration_rest_callouts)
[スタブ API を使用したモックフレームワークの作成](https://developer.salesforce.com/docs/atlas.ja-jp.218.0.apexcode.meta/apexcode/apex_testing_stub_api.htm)
[Apex スタブ API を使用してテストクラスを作成する](https://base.terrasky.co.jp/articles/4EXfR)