JMeter で Salesforce の API をコールする

[JMeter](https://jmeter.apache.org/) はクライアントサーバシステムのパフォーマンス測定および負荷テストを行うための、オープンソースの Java アプリケーションです。この記事では、JMeter で Salesforce の REST API をコールするサンプルシナリオを紹介します。

### サンプルの試し方

1. [サンプルのシナリオファイルをダウンロード](https://raw.githubusercontent.com/shunkosa/jmeter-salesforce-api-example-jp/main/salesforce-api-example.jmx)し、JMeter で開きます。(拡張子は `.jmx` で保存するよう注意してください)

2. `ログイン設定` のユーザ変数定義を開き、変数の値を更新します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/441ac241-6633-32c1-b767-f4e975bcbb78.png)

3. `Salesforce へ REST API コール` のシンプルコントローラを開き、サンプルの HTTP リクエストの内容を確認し、必要に応じて追加・修正します。サンプルでは「JMeter Example」という名前の取引先を 1 件 insert します。[『REST API 開発者ガイド』](https://developer.salesforce.com/docs/atlas.ja-jp.api_rest.meta/api_rest)も参照してください。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/6d5a87ec-a7b1-9d36-8feb-fbdc389c7873.png)

4. 再生ボタンをクリックしてシナリオを実行します。

5. `結果をツリーで表示` から HTTP レスポンスを確認します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/3cc72c99-d6d5-65cf-956f-6137e9e5d2fd.png)

### ログイン処理部分の簡単な解説

簡便のため、このサンプルでは、[SOAP API の login()](https://developer.salesforce.com/docs/atlas.ja-jp.api.meta/api/sforce_api_calls_login.htm) を用いてアクセストークン (セッション Id) を取得しています。エンドポイントは `https://login.salesforce.com/services/Soap/c/55.0/` のようになるため、これを `SalesforceへのSOAI APIリクエストデフォルト設定` で設定しています。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/379a4096-e05d-8d2b-91c3-2ff3a2bbaa78.png)

login() をコールする処理は次の `SalesforceにSOAP APIでログイン` の HTTP リクエストですが、ここではドキュメントに従い以下のリクエストを送信します。username、password、security_token は初めに設定したJMeter上の変数を指しています。

```xml
<soapenv:Envelope
  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:urn="urn:enterprise.soap.sforce.com">
    <soapenv:Header></soapenv:Header>
    <soapenv:Body>
        <urn:login>
            <urn:username>${username}</urn:username>
            <urn:password>${password}${security_token}</urn:password>
        </urn:login>
    </soapenv:Body>
</soapenv:Envelope>
```
リクエストヘッダとして、`SOAPAction` に `urn:enterprise.soap.sforce.com/Soap/loginRequest` を、`Content-Type` に `text/xml` を指定しています。

ログインに成功すると以下のようなレスポンスを受け取ります

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:enterprise.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <loginResponse>
      <result>
        <metadataServerUrl>https://yourdomain.my.salesforce.com/services/Soap/m/55.0/00Dxxxx....</metadataServerUrl>
        <passwordExpired>false</passwordExpired>
        <sandbox>false</sandbox>
        <serverUrl>https://yourdomain.my.salesforce.com/services/Soap/c/55.0/00Dxxxxx....</serverUrl>
        <sessionId>00D7Fxxxxxxx.....</sessionId>
        <userId>005xxxx.....</userId>
        <userInfo>
          <accessibilityMode>false</accessibilityMode>
          <chatterExternal>false</chatterExternal>
          <currencySymbol>￥</currencySymbol>
          <orgAttachmentFileSizeLimit>26214400</orgAttachmentFileSizeLimit>
          <orgDefaultCurrencyIsoCode>JPY</orgDefaultCurrencyIsoCode>
          <orgDefaultCurrencyLocale>ja_JP</orgDefaultCurrencyLocale>
          <orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
          <orgHasPersonAccounts>false</orgHasPersonAccounts>
          <organizationId>xxxxx</organizationId>
          <organizationMultiCurrency>false</organizationMultiCurrency>
          <organizationName>Your Org Name</organizationName>
          <profileId>00eXXXX.....</profileId>
          <roleId xsi:nil="true" />
          <sessionSecondsValid>7200</sessionSecondsValid>
          <userDefaultCurrencyIsoCode xsi:nil="true" />
          <userEmail>youremail@example.com</userEmail>
          <userFullName>Your Name</userFullName>
          <userId>005XXX.....</userId>
          <userLanguage>ja</userLanguage>
          <userLocale>en_US</userLocale>
          <userName>yourusername@example.com</userName>
          <userTimeZone>Asia/Tokyo</userTimeZone>
          <userType>Standard</userType>
          <userUiSkin>Theme3</userUiSkin>
        </userInfo>
      </result>
    </loginResponse>
  </soapenv:Body>
</soapenv:Envelope>
```

この中の `<sessionId>` と `<serverUrl>` のホスト名部分を REST API コールに使用します。`<sessionId>` は XPath でタグ内のテキストをそのまま取得。`<serverUrl>` のホスト名部分についてはセッション Idと同様に一度タグ内のテキストを取得した後、正規表現でホスト名部分だけを抽出しています。

セッション Id は `sessionId`、`<serverUrl>` のホスト名部分は `instanceurl` という名前の JMeter　上の変数として保存されていますので、これらを JMeter で REST API コールする際に使用しています。

## 参考
* [Help - Salesforce 組織のパフォーマンスの測定](https://help.salesforce.com/s/articleView?id=sf.technical_requirements_measuring_ept.htm&type=5&language=ja)
* [Youtube - J-Meter ツールを使用した Salesforce Lightning の API テスト (英語)](https://www.youtube.com/watch?v=misQxkotSw4) ※少し古いです
* [TeamSpirit さんのブログ - JMeterでSalesforceアプリへの負荷テストを実施しました](https://teamspirit.hatenablog.com/entry/2022/08/29/110000)