Apex Web サービス

学習の目的

このモジュールを完了すると、次のことができるようになります。
  • 2 種類の Apex Web サービスとこれらのサービスの概要について説明する。
  • 各 HTTP メソッドに対応するメソッドが含まれる Apex REST クラスを作成する。
  • エンドポイントを使用してカスタム Apex REST メソッドを呼び出す。
  • JSON 形式でリクエストボディを送信し、カスタム Apex REST メソッドにデータを渡す。
  • Apex REST メソッドのテストメソッドを作成して、テスト REST 要求でプロパティを設定する。
  • パラメータ値でメソッドをコールして、Apex REST メソッドのテストメソッドを作成する。

Apex クラスを Web サービスとして公開

Apex クラスメソッドを REST または SOAP Web サービス処理として公開できます。メソッドを Web 経由でコールできるようにすることで、外部アプリケーションと Salesforce を統合して、あらゆる種類の便利な操作を実行できます。

たとえば、自社のコールセンターで内部アプリケーションを使用して、社内リソースを管理しているとします。カスタマーサポート担当者は、同じアプリケーションを使用して日常業務 (Salesforce のケースレコードの管理など) も行えるようになります。担当者は、1 つのインターフェースを使用して、ケースレコードの表示や更新を行ったり、内部リソースにアクセスしたりできます。アプリケーションは、Apex Web サービスクラスをコールして、Salesforce ケースレコードを管理します。

REST サービスとしてクラスを公開

Apex クラスを REST Web サービスとして使用できるようにすることは簡単です。クラスをグローバルクラス、メソッドをグローバル静的メソッドとして定義します。アノテーションをクラスおよびメソッドに追加します。たとえば、このサンプル Apex REST クラスでは 1 つのメソッドを使用します。getRecord メソッドは、カスタム REST API コールです。これは、@HttpGet アノテーションが付加されており、GET 要求で呼び出されます。

@RestResource(urlMapping='/Account/*')
global with sharing class MyRestResource {
    @HttpGet
    global static Account getRecord() {
        // Add your code
    }
}

ご覧のとおり、クラスには @RestResource(urlMapping='/Account/*') アノテーションが付加されています。Apex REST のベースエンドポイントは、https://yourInstance.salesforce.com/services/apexrest/ です。URL 対応付けがベースエンドポイントに追加されて、REST サービスのエンドポイントが形成されます。たとえば、このクラスの例の REST エンドポイントは https://yourInstance.salesforce.com/services/apexrest/ です。お客様の組織では、https://yourInstance.salesforce.com/services/apexrest/Account/* のようになります。

URL 対応付けでは大文字と小文字が区別され、ワイルドカード (*) を含めることができます。

global static として公開する各メソッドを定義し、HTTP メソッドと関連付けるアノテーションを追加します。次のアノテーションを使用できます。それぞれのアノテーションは、各 Apex クラスで 1 回のみ使用できます。

アノテーション アクション 詳細
@HttpGet 参照 レコードを参照または取得します。
@HttpPost 作成 レコードを作成します。
@HttpDelete 削除 レコードを削除します。
@HttpPut 更新/挿入 通常、既存のレコードを更新したり、レコードを作成したりするために使用します。
@HttpPatch 更新 通常、既存のレコードの項目を更新するために使用します。

SOAP サービスとしてクラスを公開

Apex クラスを SOAP Web サービスとして使用できるようにすることは REST と同様に簡単です。クラスをグローバルクラスとして定義します。公開する各メソッドに webservice キーワードと static 定義修飾子を追加します。webservice キーワードを追加したメソッドにはグローバルアクセス権が付与されます。

たとえば、次に 1 つのメソッドがあるサンプルクラスを示します。getRecord メソッドは、取引先レコードを返すカスタム SOAP API コールです。

global with sharing class MySOAPWebService {
    webservice static Account getRecord(String id) {
        // Add your code
    }
}

外部アプリケーションは、クラス WSDL ファイルを使用して、カスタム Apex メソッドを Web サービス処理としてコールできます。[設定] の [Apex クラス] ページからアクセスするクラスの詳細ページで、クラスのこの WSDL を生成します。通常、WSDL ファイルをサードパーティ開発者に送信 (または自身で使用) して、Web サービスのインテグレーションを作成します。

プラットフォームセキュリティは、Salesforce の最重要事項であるため、Web サービスから認証が要求されます。外部アプリケーションは、Apex クラスの WSDL の他に Enterprise WSDL または Partner WSDL を使用してログイン機能を実現する必要があります。

Apex REST のウォークスルー

では、本題に入りましょう。次のいくつかのステップでは、Apex REST サービスを作成するプロセスについて説明します。まず、REST サービスとして公開する Apex クラスを作成します。次に、クライアントからいくつかのメソッドをコールし、最後に単体テストを作成します。かなり多くのコードがありますが、労力を費やす価値はあります。

Apex クラスは、ケースレコードを管理します。クラスには 5 個のメソッドがあり、各メソッドは HTTP メソッドに対応します。たとえば、クライアントアプリケーションが GET HTTP メソッドの REST コールを呼び出す場合、getCaseById メソッドが呼び出されます。

クラスは /Cases/* の URL 対応付けで定義されているので、この REST サービスをコールするために使用されるエンドポイントは、https://yourInstance.salesforce.com/services/apexrest/Cases/ で始まる URI になります。

既存のコードを壊すことなく機能をアップグレードできるように、API エンドポイントのバージョン管理についても検討することをお勧めします。/Cases/v1/*/Cases/v2/* の URL 対応付けを指定する 2 つのクラスを作成して、この機能を実装できます。

では、Apex REST クラスの作成から始めましょう。

  1. 設定ギア (設定ギアアイコン) から開発者コンソールを開きます。
  2. 開発者コンソールで、[File (ファイル)] | [New (新規)] | [Apex Class (Apex クラス)] を選択します。
  3. クラス名として「CaseManager」と入力し、[OK] をクリックします。
  4. 自動生成されるコードを次のクラス定義で置き換えます。
    @RestResource(urlMapping='/Cases/*')
    global with sharing class CaseManager {
    
        @HttpGet
        global static Case getCaseById() {
            RestRequest request = RestContext.request;
            // grab the caseId from the end of the URL
            String caseId = request.requestURI.substring(
              request.requestURI.lastIndexOf('/')+1);
            Case result =  [SELECT CaseNumber,Subject,Status,Origin,Priority
                            FROM Case
                            WHERE Id = :caseId];
            return result;
        }
    
        @HttpPost
        global static ID createCase(String subject, String status,
            String origin, String priority) {
            Case thisCase = new Case(
                Subject=subject,
                Status=status,
                Origin=origin,
                Priority=priority);
            insert thisCase;
            return thisCase.Id;
        }   
    
        @HttpDelete
        global static void deleteCase() {
            RestRequest request = RestContext.request;
            String caseId = request.requestURI.substring(
                request.requestURI.lastIndexOf('/')+1);
            Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
            delete thisCase;
        }     
    
        @HttpPut
        global static ID upsertCase(String subject, String status,
            String origin, String priority, String id) {
            Case thisCase = new Case(
                    Id=id,
                    Subject=subject,
                    Status=status,
                    Origin=origin,
                    Priority=priority);
            // Match case by Id, if present.
            // Otherwise, create new case.
            upsert thisCase;
            // Return the case ID.
            return thisCase.Id;
        }
    
        @HttpPatch
        global static ID updateCaseFields() {
            RestRequest request = RestContext.request;
            String caseId = request.requestURI.substring(
                request.requestURI.lastIndexOf('/')+1);
            Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
            // Deserialize the JSON string into name-value pairs
            Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.tostring());
            // Iterate through each parameter field and value
            for(String fieldName : params.keySet()) {
                // Set the field and value on the Case sObject
                thisCase.put(fieldName, params.get(fieldName));
            }
            update thisCase;
            return thisCase.Id;
        }    
    
    }
  5. CTRL+S キーを押して保存します。

POST メソッドを使用したレコードの作成

では、作成した Apex REST クラスを使用してみましょう。まず、POST メソッドをコールしてケースレコードを作成します。

REST サービスを呼び出すには、REST クライアントを使用する必要があります。独自の API クライアント、cURL コマンドラインツール、PHP の cURL ライブラリなど、ほぼすべての REST クライアントを使用できます。REST クライアントアプリケーションとしてワークベンチツールを使用しますが、後で cURL についても少し触れます。

Apex REST は、リソースを表現するために、JSON と XML の 2 つの形式をサポートしています。JSON による表現は、要求またはレスポンスボディにデフォルトで渡され、形式は、HTTP ヘッダーの Content-Type プロパティで示されます。XML よりも JSON の方が読みやすく理解しやすいので、この単元では JSON のみを使用します。このステップでは、JSON 形式でケースレコードを送信します。

Apex REST では、OAuth 2.0 とセッション認証メカニズムがサポートされています。簡単に言えば、これは業界標準を使用して、アプリケーションとデータを安全に保持するということです。幸いにも、ワークベンチを使用して簡単にテストを実行できます。ワークベンチは、システム管理者および開発者が Lightning Platform API を使用して組織とやりとりするための強力な Web ベースのツールです。ワークベンチでは、ユーザ名とパスワードで Salesforce にログインするときにセッション認証を使用します。次に、REST Explorer を使用して REST サービスをコールします。

  1. https://workbench.developerforce.com/login.php に移動します。
  2. [Environment (環境)] で [Production (本番)] を選択します。
  3. [API Version (API バージョン)] ドロップダウンから最新の API バージョンを選択します。
  4. サービスの利用規約に同意し、[Salesforce でログイン] をクリックします。
  5. ワークベンチが自分の情報にアクセスできるようにするには、[Allow (許可)] をクリックします。
  6. ログイン情報を入力し、[Log in to Salesforce (Salesforce へのログイン)] をクリックします。
  7. ログインしたら、[utilities (ユーティリティ)] | [REST Explorer] を選択します。
  8. [POST] を選択します。
  9. REST Explorer では、組織のインスタンス URL に対する相対パスを指定する必要があるため、インスタンス URL に追加するパスのみを入力します。相対 URI の入力項目で、デフォルトの URL を /services/apexrest/Cases/ で置き換えます。
  10. リクエストボディに、挿入するオブジェクトを示す次の JSON 文字列を挿入します。
    {
      "subject" : "Bigfoot Sighting!",
      "status" : "New",
      "origin" : "Phone",
      "priority" : "Low"
    }
  11. [実行] をクリックします。
    この呼び出しでは、POST HTTP メソッドに関連付けられたメソッド (createCase メソッド) をコールします。
  12. 返された応答を表示するには、[Show Raw Response (未加工の応答を表示)] をクリックします。
    返された応答は、次のようになります。応答には、新しいケースレコードの ID が含まれます。あなたの ID 値は 50061000000t7kYAAQ とは違う可能性があります。次のステップで使用するために ID 値を保存します。
    HTTP/1.1 200 OK
    Date: Wed, 07 Oct 2015 14:18:20 GMT
    Set-Cookie: BrowserId=F1wxIhHPQHCXp6wrvqToXA;Path=/;Domain=.salesforce.com;Expires=Sun, 06-Dec-2015 14:18:20 GMT
    Expires: Thu, 01 Jan 1970 00:00:00 GMT
    Content-Type: application/json;charset=UTF-8
    Content-Encoding: gzip
    Transfer-Encoding: chunked
    
    "50061000000t7kYAAQ" 

カスタム GET メソッドを使用したデータの取得

前回と同様の手順に従い、ワークベンチを使用して GET HTTP メソッドを呼び出します。

  1. ワークベンチで、[GET] を選択します。
  2. URI /services/apexrest/Cases/<Record ID> を入力します。<Record ID> は、前のステップで作成したレコードの ID に置き換えます。
  3. [実行] をクリックします。

    この呼び出しでは、GET HTTP メソッドに関連付けられたメソッド (getCaseById メソッド) をコールします。

  4. 返された応答を表示するには、[Show Raw Response (未加工の応答を表示)] をクリックします。

    返された応答は、次のようになります。応答には、メソッドが新しいケースレコードを照会した項目が含まれます。

    HTTP/1.1 200 OK
    Date: Wed, 07 Oct 2015 14:28:20 GMT
    Set-Cookie: BrowserId=j5qAnPDdRxSu8eHGqaRVLQ;Path=/;Domain=.salesforce.com;Expires=Sun, 06-Dec-2015 14:28:20 GMT
    Expires: Thu, 01 Jan 1970 00:00:00 GMT
    Content-Type: application/json;charset=UTF-8
    Content-Encoding: gzip
    Transfer-Encoding: chunked
    
    {
      "attributes" : {
        "type" : "Case",
        "url" : "/services/data/v34.0/sobjects/Case/50061000000t7kYAAQ"
      },
      "CaseNumber" : "00001026",
      "Subject" : "Bigfoot Sighting!",
      "Status" : "New",
      "Origin" : "Phone",
      "Priority" : "Low",
      "Id" : "50061000000t7kYAAQ"
    }
    

cURL を使用したデータの取得

優れた開発者なら誰でも、1) アイスクリームを食べる自分のアニメーション GIF を作成する方法、2) 円周率 25 桁までの値、3) cURL の使用方法くらいは知っています。最初の 2 つはこのモジュールでは取り上げませんので、最後の 1 つに焦点を絞りましょう。

cURL は、URL 構文を使用してファイルを取得または送信するためのコマンドラインツールです。このツールは、REST エンドポイントを使用して作業するときに非常に役に立ちます。Apex REST サービスのワークベンチを使用する代わりに、cURL を使用して GET HTTP メソッドを呼び出します。REST エンドポイントを「cURL」するたびに、認証用のセッション ID を渡します。ワークベンチで作業する場合は、ログインすると内部的にセッション ID が渡されます。

セッション ID を取得するには、まず Salesforce 組織で接続アプリケーションを作成し、OAuth を有効にします。クライアントアプリケーション (この場合は cURL) は、接続アプリケーションを使用して Salesforce に接続します。こちらの手順に従って、セッション ID を取得するために必要なコンシューマ鍵とコンシューマの秘密を提供する接続アプリケーションを作成します。接続アプリケーションの OAuth 範囲を選択するときは、「データへのアクセスと管理 (api)」範囲を選択します。接続アプリケーションによる設定が完了するまで 5 ~ 10 分かかる場合があります。準備ができたら、自分のログイン情報と接続アプリケーションを使用して次の cURL コマンドを実行します。

任意のコマンドラインツール (macOS/Linux のターミナル、Windows のコマンドプロンプトなど) を使用して、これらの要求を実行します。

curl -v https://login.salesforce.com/services/oauth2/token -d "grant_type=password" -d "client_id=<your_consumer_key>" -d "client_secret=<your_consumer_secret>" -d "username=<your_username>" -d "password=<your_password_and_security_token>" -H 'X-PrettyPrint:1'

すべて成功すると、結果にはセッション ID である access_token と組織の instance_url が含まれます。

アクセストークンが含まれた cURL 応答

続いて、次のような cURL コマンドを入力します。このコマンドは Apex REST サービスをコールし、ケース情報を返します。

curl https://yourInstance.salesforce.com/services/apexrest/Cases/<Record_ID> -H 'Authorization: Bearer <your_session_id>' -H "X-PrettyPrint:1"

Enter キーを押すと、次のような画面が表示されます。これでコマンドラインを習得できたので、cURL、jq、sed、awk、grep を思う存分遠慮なくご使用ください。cURL についての詳細は、「リソース」セクションを参照してください。

コマンドラインからの cURL と応答

カスタム PUT または PATCH メソッドを使用したデータの更新

PUT または PATCH HTTP メソッドを使用してレコードを更新できます。PUT メソッドは、リソース全体を更新するか (存在する場合)、リソースを作成します (存在しない場合)。PUT は、本質的に upsert メソッドです。PATCH メソッドは、既存のリソースの指定した部分のみを更新します。Apex の更新操作では、指定した項目のみが更新され、レコード全体は上書きされません。メソッドの操作 (更新または更新/挿入) を判別するいくつかの Apex コードを作成します。

PUT メソッドを使用したデータの更新

CaseManager クラスに追加した upsertCase メソッドは、PUT アクションを実装します。ここに記載されているこのメソッドは参照用です。このメソッドは組み込みの upsert Apex DML メソッドを使用し、ID 値を照合してケースレコード項目を作成または上書きします。ID がリクエストボディで送信された場合、その ID を使用してケースの sObject が入力されます。ID が送信されない場合は、ID を使用せずにケースの sObject が作成されます。入力されるケースの sObject で upsert メソッドが呼び出され、DML ステートメントで残りの作業が行われます。成功です。

@HttpPut
global static ID upsertCase(String subject, String status,
    String origin, String priority, String id) {
    Case thisCase = new Case(
        Id=id,
        Subject=subject,
        Status=status,
        Origin=origin,
        Priority=priority);
    // Match case by Id, if present.
    // Otherwise, create new case.
    upsert thisCase;
    // Return the case ID.
    return thisCase.Id;
}
PUT メソッドを呼び出す手順は、次のとおりです。
  1. ワークベンチ REST Explorer で、[PUT] を選択します。
  2. URI として「/services/apexrest/Cases/」と入力します。
  3. upsertCase メソッドでは、項目値がリクエストボディで渡されます。リクエストボディに次の内容を追加して、<Record ID> を前に作成したケースレコードの ID に置き換えます。
    {
      "id": "<Record_ID>",
      "status" : "Working",
      "subject" : "Bigfoot Sighting!",
      "priority" : "Medium"
    }
    メモ

    メモ

    ID 項目は省略可能です。ケースレコードを作成する場合は、この項目を省略します。この例では、ケースレコードを更新するのでこの項目を渡します。

  4. [実行] をクリックします。

    この要求は、REST サービスから upsertCase メソッドを呼び出します。[状況][件名][優先度] 項目が更新されます。件名は、古い値と同じでも更新されます。また、リクエストボディには、[発生源] 項目の値が含まれていないため、upsertCase メソッドの origin パラメータは null になります。その結果、レコードが更新されると、[発生源] 項目がクリアされます。

    これらの項目を確認するには、https://yourInstance.salesforce.com/<Record ID> に移動して Salesforce のこのレコードを表示します。

PATCH メソッドを使用したデータの更新

PUT メソッドの代わりに PATCH メソッド使用してレコード項目を更新できます。PATCH メソッドはさまざまな方法で実装できます。その方法の 1 つが、メソッドでパラメータを指定して各項目を更新することです。たとえば、updateCasePriority(String priority) という署名を使用して、ケースの優先度を更新するメソッドを作成できます。複数の項目を更新するには、目的のすべての項目をパラメータとしてリストします。

または、リクエストボディで項目を JSON の名前/値ペアとして渡します。後者の方が柔軟性が高くなります。こうすることで、メソッドは任意の数のパラメータを受け入れることができ、パラメータがメソッドの署名で固定されなくなります。この方法には、null になることが原因で誤って項目がクリアされることがないという利点もあります。CaseManager クラスに追加した updateCaseFields メソッドは、この 2 番目の方法を使用します。この方法では、リクエストボディの JSON 文字列を名前/値ペアの対応付けに並列化し、sObject PUT メソッドを使用して項目を設定します。

@HttpPatch
global static ID updateCaseFields() {
    RestRequest request = RestContext.request;
    String caseId = request.requestURI.substring(
        request.requestURI.lastIndexOf('/')+1);
    Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
    // Deserialize the JSON string into name-value pairs
    Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.tostring());
    // Iterate through each parameter field and value
    for(String fieldName : params.keySet()) {
        // Set the field and value on the Case sObject
        thisCase.put(fieldName, params.get(fieldName));
    }
    update thisCase;
    return thisCase.Id;
}
PATCH メソッドを呼び出す手順は、次のとおりです。
  1. ワークベンチ REST Explorer で、[PATCH] をクリックします。
  2. URI として「/services/apexrest/Cases/<Record ID>と入力します。<Record ID> を、前に作成したケースレコードの ID に置き換えます。次の JSON をリクエストボディに入力します。
    {
      "status" : "Escalated",
      "priority" : "High"
    }

    この JSON には、statuspriority の 2 つの項目値があります。updateCaseFields メソッドは、送信された JSON からこれらの値を取得します。これらの値は、更新するオブジェクト項目を指定するために使用されます。

  3. [実行] をクリックします。

    この要求は、REST サービスで updateCaseFields メソッドを呼び出します。ケースレコードの [状況] および [優先度] 項目が新しい値に更新されます。これらの項目を確認するには、https://yourInstance.salesforce.com/<Record ID> に移動して Salesforce のこのレコードを表示します。

Apex REST クラスのテスト

Apex REST クラスのテストは他の Apex クラスのテストに似ており、パラメータ値を渡してクラスメソッドをコールし、結果を検証するだけです。パラメータを取らないメソッドや、REST 要求の情報を使用するメソッドの場合は、テスト REST 要求を作成します。

次に、Apex REST サービスをテストする一般的な方法を示します。REST 要求をシミュレーションするには、次のようにテストメソッドで RestRequest を作成し、要求のプロパティを設定します。また、要求で「渡す」パラメータを追加して URI パラメータをシミュレーションすることもできます。

// Set up a test request
RestRequest request = new RestRequest();

// Set request properties
request.requestUri =
    'https://yourInstance.salesforce.com/services/apexrest/Cases/'
    + recordId;
request.httpMethod = 'GET';

// Set other properties, such as parameters
request.params.put('status', 'Working');

// more awesome code here....
// Finally, assign the request to RestContext if used
RestContext.request = request;

テストするメソッドが RestContext を介して要求値にアクセスする場合、要求を RestContext に割り当てて、要求 (RestContext.request = request;) を入力します。

次に、開発者コンソールでクラス全体を保存し、結果を実行します。

  1. 開発者コンソールで、[File (ファイル)] | [New (新規)] | [Apex Class (Apex クラス)] を選択します。
  2. クラス名として「CaseManagerTest」と入力し、[OK] をクリックします。
  3. 自動生成されるコードを次のクラス定義で置き換えます。
    @IsTest
    private class CaseManagerTest {
    
        @isTest static void testGetCaseById() {
            Id recordId = createTestRecord();
            // Set up a test request
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'GET';
            RestContext.request = request;
            // Call the method to test
            Case thisCase = CaseManager.getCaseById();
            // Verify results
            System.assert(thisCase != null);
            System.assertEquals('Test record', thisCase.Subject);
        }
    
        @isTest static void testCreateCase() {
            // Call the method to test
            ID thisCaseId = CaseManager.createCase(
                'Ferocious chipmunk', 'New', 'Phone', 'Low');
            // Verify results
            System.assert(thisCaseId != null);
            Case thisCase = [SELECT Id,Subject FROM Case WHERE Id=:thisCaseId];
            System.assert(thisCase != null);
            System.assertEquals(thisCase.Subject, 'Ferocious chipmunk');
        }
    
        @isTest static void testDeleteCase() {
            Id recordId = createTestRecord();
            // Set up a test request
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'GET';
            RestContext.request = request;
            // Call the method to test
            CaseManager.deleteCase();
            // Verify record is deleted
            List<Case> cases = [SELECT Id FROM Case WHERE Id=:recordId];
            System.assert(cases.size() == 0);
        }
    
        @isTest static void testUpsertCase() {
            // 1. Insert new record
            ID case1Id = CaseManager.upsertCase(
                    'Ferocious chipmunk', 'New', 'Phone', 'Low', null);
            // Verify new record was created
            System.assert(Case1Id != null);
            Case case1 = [SELECT Id,Subject FROM Case WHERE Id=:case1Id];
            System.assert(case1 != null);
            System.assertEquals(case1.Subject, 'Ferocious chipmunk');
            // 2. Update status of existing record to Working
            ID case2Id = CaseManager.upsertCase(
                    'Ferocious chipmunk', 'Working', 'Phone', 'Low', case1Id);
            // Verify record was updated
            System.assertEquals(case1Id, case2Id);
            Case case2 = [SELECT Id,Status FROM Case WHERE Id=:case2Id];
            System.assert(case2 != null);
            System.assertEquals(case2.Status, 'Working');
        }    
    
        @isTest static void testUpdateCaseFields() {
            Id recordId = createTestRecord();
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'PATCH';
            request.addHeader('Content-Type', 'application/json');
            request.requestBody = Blob.valueOf('{"status": "Working"}');
            RestContext.request = request;
            // Update status of existing record to Working
            ID thisCaseId = CaseManager.updateCaseFields();
            // Verify record was updated
            System.assert(thisCaseId != null);
            Case thisCase = [SELECT Id,Status FROM Case WHERE Id=:thisCaseId];
            System.assert(thisCase != null);
            System.assertEquals(thisCase.Status, 'Working');
        }  
    
        // Helper method
        static Id createTestRecord() {
            // Create test record
            Case caseTest = new Case(
                Subject='Test record',
                Status='New',
                Origin='Phone',
                Priority='Medium');
            insert caseTest;
            return caseTest.Id;
        }          
    
    }
  4. CTRL+S キーを押して保存します。
  5. [Test (テスト)] | [Run All (すべて実行)] を選択して、組織のすべてのテストを実行します。

[Tests (テスト)] タブにテスト結果が表示されます。テスト実行が完了したら、[Overall Code Coverage (全体のコードカバー率)] ペインで CaseManager 行を確認します。カバー率が 100% になっています。

もうひとこと...

Apex REST でサポートされているデータ型と名前空間、Salesforce API、およびセキュリティに関する考慮事項について説明します。
Apex REST でサポートされているデータ型
Apex REST では、パラメータおよび戻り値で次のデータ型がサポートされています。
  • Apex プリミティブ (sObject と Blob を除く)。
  • sObject
  • Apex プリミティブまたは sObject のリストまたは対応付け (String キーの対応付けのみをサポート)。
  • 上記の型のメンバー変数を含むユーザ定義型。
Apex REST エンドポイントの名前空間
Apex REST メソッドは、管理パッケージと未管理パッケージで使用できます。管理パッケージに含まれる Apex REST メソッドをコールする場合、REST のコール URL に管理パッケージの名前空間を含める必要があります。たとえば、packageNamespace という管理パッケージ名前空間にクラスが含まれており、Apex REST メソッドが /MyMethod/* という URL 対応付けを使用している場合、これらのメソッドをコールするために REST によって使用される URL は、https://instance.salesforce.com/services/apexrest/packageNamespace/MyMethod/ という形式になります。
カスタム Apex Web サービスと Salesforce API
REST および SOAP サービスのカスタム Apex コードを使用する代わりに、Salesforce の REST および SOAP API を使用して、外部アプリケーションを Salesforce と統合できます。これらの API では、レコードの作成、更新、削除ができます。ただし、Apex Web サービスを使用する利点は、Apex メソッドで複雑なロジックをカプセル化できることです。このロジックは、消費アプリケーションから隠ぺいされます。また、クライアントと Salesforce サーバ間で実行される往復処理が少なくなるため、Apex クラスの処理が個々の API コールを実行するよりも高速になる可能性があります。Apex Web サービスコールでは、1 つの要求のみが送信され、メソッド内の処理はすべてサーバで実行されます。
Apex Web サービスのセキュリティに関する考慮事項
Apex Web サービスメソッドが実行されるセキュリティコンテキストは、Salesforce API のセキュリティコンテキストとは異なります。Salesforce API とは異なり、Apex Web サービスメソッドはシステム権限で実行され、ユーザのオブジェクト権限や項目権限は考慮されません。ただし、Apex Web サービスメソッドが with sharing キーワードで宣言されている場合は、共有ルールが適用されます。