【Salesforse】【Apex】テストクラスで気を付けていることまとめ

人に伝える機会も増えてきたのでまとめます。
最後に[悪い例と良い例のサンプルコード](#サンプルコード)も載せています(\*'ω'*)

# testMethodではなくisTestを使用する

**参考：[IsTest アノテーション](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_annotation_isTest.htm)**

>Salesforce では、testMethod ではなく @IsTest を使用することをベストプラクティスとして推奨しています。testMethod キーワードは今後のリリースで廃止される可能性があります。

古いソースコード見ると残っていたりします。
新たに作られたテストクラスでtestMethod書いてたら、、( ^)o(^ )おこ

# isTest(SeeAllData=True)をむやみやたらに使用しない
**参考：[isTest(SeeAllData=True) アノテーションの使用](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_testing_seealldata_using.htm)**

原則使わない。がいいと思います。特別な場合ってほんとに極稀です。

# TestSetupアノテーションを使用する

テストメソッドで毎度毎度データ作っていると実行時間がかなり長くなりますので
TestSetupを使用することをおすすめします。以下の記事で実証しています！

https://qiita.com/piepie1231/items/b22535f39c69f8f2b034

# System.runAs()を使用する

**参考：[runAs メソッドの使用](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_testing_tools_runas.htm)**

指定しない場合はテストを実行したユーザ（システム管理者）になるので、実際に運用するプロファイルのユーザを指定してあげます。
このユーザもtestSetupで作ってあげるといいでしょう。

# 1テストメソッド1テストとする

1メソッド内で色々呼び出して、何のテストをしているのか分からないのをたまに見かけますが
それはナンセンスかと思います。あくまで単体テストなので。

# startTestとstopTestを使用する

**参考：[Limits、startTest、および stopTest の使用](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_testing_tools_start_stop_test.htm)**

テストメソッドの最初と最後に入れているのをたまに見かけますが
適当に入れたことバレるのでやめたほうがいいですw
<font color="Red">※これは具体的に解説したいので後日記事にする予定</font>


# アサーションを入れる

**参考：[System クラス](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_system.htm)**
　　　**[明確かつ計画的な Apex アサーションの記述](https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_new_assert_class.htm&release=240&type=5)**
　　　**[Assert Class](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_System_Assert.htm)**

アサーションがないのはテストしてないも同義ではないかと思います。（過激発言）

* 戻り値があればそれを
* InsertやDeleteがあるならレコード数を
* updateがあるなら更新した項目を

くらいは最低限見るべきかと思います。
最近はWinter'23でリリースされたAssertクラスを個人的には使っています。


# VSCodeでデバッグする

System.debug()を使うのもいいですが、デバッガあるので使いましょ。
設定方法は以下にまとめています。

https://qiita.com/piepie1231/items/def2e6b0f8e5d16f63f7

https://qiita.com/piepie1231/items/b2e2ca1153ed0857df97

# サンプルコード
※両例ともに当記事の指摘箇所についてのみコメントしています。

```Apex:悪い例
@isTest(SeeAllData=True)// @isTest(SeeAllData=True)を使用している(組織のデータを取得している)
private class piepie001BadTest {

    // @TestSetupを使用していない

    static testMethod void InsertOpportunityTest() {// testMethodを使用している

        Account acc = [SELECT Id FROM Account WHERE Name = 'ぴえ株式会社'];

        // System.runAs()を使用していない
        // 1テストメソッドで複数テストを実施している
        // startTestとstopTestを使用していない
        piepie001.InsertOpportunity(acc.Id, 1);
        piepie001.InsertOpportunity(acc.Id, 2);
        piepie001.InsertOpportunity(acc.Id, 3);

        // アサーションがない
    }
}
```
```Apex:良い例
@isTest // @isTest(SeeAllData=True)を使用していない
private class piepie001Test {
    
    @TestSetup // @TestSetupを使用している
    static void setup(){
        Profile p = [Select Id from Profile Where Name = '営業'];
        User u = TestDataFactory.createUser('test1', p.Id, true);
        Account acc = TestDataFactory.createAccount(true);
    }

    @isTest // @isTestを使用している
    static void InsertOpportunity1() {
        User u = [SELECT Id FROM User WHERE Name = 'test1' LIMIT 1];
        Account acc = [SELECT Id FROM Account LIMIT 1];

        // 1テストメソッドで1テストを実施している
        Test.startTest();// startTestとstopTestを使用している
        system.runAs(u){ // System.runAs()を使用している
            piepie001.InsertOpportunity(acc.Id, 1);
        }
        Test.stopTest();

        // アサーションがある
        Opportunity opp = [SELECT Id, StageName FROM Opportunity LIMIT 1];
        Assert.areEqual('新規', opp.StageName);
    }

    @isTest // @isTestを使用している
    static void InsertOpportunity2() {
        User u = [SELECT Id FROM User WHERE Name = 'test1' LIMIT 1];
        Account acc = [SELECT Id FROM Account LIMIT 1];

        // 1テストメソッドで1テストを実施している
        Test.startTest();// startTestとstopTestを使用している
        system.runAs(u){ // System.runAs()を使用している
            piepie001.InsertOpportunity(acc.Id, 2);
        }
        Test.stopTest();

        // アサーションがある
        Opportunity opp = [SELECT Id, StageName FROM Opportunity LIMIT 1];
        Assert.areEqual('成立', opp.StageName);
    }

    @isTest // @isTestを使用している
    static void InsertOpportunity3() {
        User u = [SELECT Id FROM User WHERE Name = 'test1' LIMIT 1];
        Account acc = [SELECT Id FROM Account LIMIT 1];

        // 1テストメソッドで1テストを実施している
        Test.startTest();// startTestとstopTestを使用している
        system.runAs(u){ // System.runAs()を使用している
            piepie001.InsertOpportunity(acc.Id, 3);
        }
        Test.stopTest();

        // アサーションがある
        Opportunity opp = [SELECT Id, StageName FROM Opportunity LIMIT 1];
        Assert.areEqual('失注', opp.StageName);
    }
}
```


てな感じでテストクラスは適当に書くこともできますが
色々気にすると興味深いですよね(\*'ω'*)