Salesforce開発で使用しているコード規約を晒す

# 概要

私が関わっているSalesforceプロジェクト（Classicベース）のコード規約を備忘録として公開します
SalesforceのApexはJavaライクな言語なので、基本的にJavaのコード規約に従ってよいかと思います
それに加えてSalesforce特有の機能に関する規約を加えればよいかと思います
参考になれば幸いです

# コード規約

## 共通

- 命名記法は[キャメルケース](https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A1%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9)
- 固有名詞や略語の場合はすべて大文字でも問題ない
    例）DB、APP

- [定数名](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/langCon_apex_constants.htm)、[enums](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/langCon_apex_enums.htm)型の値はすべて大文字
- 変数名は英名とする
- 第3者が直感的に用途や意味を理解できる名前
- クラス名、メソッド名、変数名に数字を含めない（テストクラス内の変数、略語としての変数を除く）
- 非管理パッケージの場合は、クラス名、Visualforce名、オブジェクト名、静的リソース名の先頭にシステムの略語をつけましょう
    複数のシステムが混在している組織においては同一システムのリソースを一覧で見やすくすることができる。また、変更セット作成する際の選択ミスを減らすことができる
    管理パッケージの場合は名前空間でシステムの区別ができるのでこのルールに従う必要はない
    例）

    ```
    hogeというシステムの場合

    hoge_callOutService.cls
    hoge_confirmPage.page
    ```
- 文字コードは**utf-8**、改行コードは**LF**とする(.vscodeで設定済み)
- 末尾の空白文字は削除する(.vscodeで設定済み)
- タブスペースは空白スペースにする(.vscodeで設定済み)

     ```json:.vscode/settings.json
     {
       "files.eol": "\n",
       "editor.renderWhitespace": "all",
       "editor.trimAutoWhitespace": true,
       "editor.insertSpaces": true,
       "files.encoding": "utf8"
     }
     ```
- テスタブルなコードを心がけよう
    例）クラス内の共通変数を使いまわすのではなく、メソッドの引数で渡すようにする

## クラス

- クラス名は名詞

## メソッド

- メソッド名は動詞
- 可読性を考慮して、100行を超える場合は分割を考えましょう

## 変数

- 変数名は名詞

## テストメソッド

テストの網羅性を管理するために、テストの一覧表をスプレッドシートやExcelなりに管理することをお勧めします
一覧表では主にケースの目的のみに留まり、詳細のパラメータ等はテストコードで示すのが2重管理を減らすことになるかと思います

- メソッド名は **[テスト対象のメソッド名]_[テスト内容]** とする
  テスト対象のプライベートメソッドは`@TestVisible`を付与することでテストメソッドからアクセスできるようになる 
  ただし、旧コードでは`@TestVisible`を使用していない（Salesforce未リリースのため）
  テストメソッドは`@isTest`を付与し、コメントにテスト内容を記載し、`@description テスト対象メソッド名`を記載
    例）

    ```
        // テスト対象のメソッド
        @TestVisible
        private static Boolean hogehoge() {
            return true;
        }

        // @description hogehoge
        // XXXが空の場合は、〇〇であることを確認
        @isTest
        static void hogehoge_xxxIsEmpty() {
            中略
        }
    ```
- 単体テストではメソッドのINPUT（引数、前提データ）がビジネスロジックによって正しいOUTPUT(戻り値、保存されるデータ)であることを検証する
  また、エラーパターン（引数がnull、空など）もテストすること
  さらに、前提データがある場合は、前提データが正しいことも事前に確認することで、前提データが変わった際の潜在的なバグを防ぐことができる
    例）

    ```
        // スコア計算
        public static Integer calcScore(Integer score) {
            Integer result = 0;
            Decimal rate = 1.5;
            // エラーパターン
            // 不正値
            if (score == null) {
                throw new MyException('score is undefined.');
            }
            // ビジネスロジック
            // ゼロ以上の場合は計算
            if(score > 0) {
                result = score * rate;
            }
            return result;
        }

        @isTest
        static void calcScore_scoreIsNull() {
            try {
                // テスト開始
                System.Test.startTest();
                calcScore(null);
                System.Test.stopTest();

                // エラー発生せずに通過した場合は、エラーとする
                System.assert(false);
            } catch (Exception e) {
                System.assert(true);
                // 確認処理(エラーメッセージが正しいこと)
                System.assertEquals('score is undefined.',　e.getMessage());
            }
        }

        // @description calcScore
        // 引数がゼロの場合は、結果がゼロであることを確認
        @isTest
        static void calcScore_scoreIsZero() {
            // 予想される結果
            Integer expectResult = 0;
            // テストパラメータ
            Integer score = 0;

            // テスト開始
            System.Test.startTest();
            Integer result = calcScore(score);
            System.Test.stopTest();

            // 結果がゼロであることを確認
            System.assertEquals(expectResult, result);
        }
    ```
- テストコードでは原則 `@isTest(SeeAllData=true)`は使用しない（やむを得ない場合は要相談）
- 保守性の高いテストコードであること
    例） hogesにhoge0が含まれていることを確認するテストメソッド

    ```
    // NGのパターン
    // テストデータによって、都度修正しなければない
    List<String> hoges = hogehoge();
    System.assertEquals('hoge0', hoges[0]);
    
    // OKパターン
    // for文を用いることで、データ量や結果によって改修しなくてよくなる
    List<String> expectResult = new List<String>{'hoge0', 'hoge1'};
    List<String> hoges = hogehoge();
    for (String hoge : hoges) {
        // 期待値（expectResult）が含まれていることを確認
        System.assert(expectResult.contains(hoge));
    }
    ```
- テストデータはtestdataFactoryクラス内で実装
- 共通データの投入は **[@testSetup](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_annotation_testsetup.htm)**で行い、
例外ケースや特殊ケースにおいては初期データ投入後、各ケース内で投入済みデータを更新や削除する
- テストケースはExcelもしくはスプレッドシートで管理
    テストケース管理シートを作成し、そのケースに基づいてコードを起こす
    コードにはケース番号を記載し、関連付けする
    管理シートには、テストの目的、前提条件、確認事項、投入データなどを記載する
    二重管理になってしまい正直嫌だけど、テストコードのみだとテストケースが網羅されているのを俯瞰するのが難しいことからこのような方法で管理しています