+ Start a Discussion
k_ogawak_ogawa 

単体テストについて

お世話になっております。

現在、Apexクラスの単体テストを行っているのですが、単体テストの法方が
理解できておらず、何をしたらよいのかわからない状態です。

テストクラスの作成はできたのですが、どのセンテンスについて何をやればよいのか、
またその記述方法など・・・
いろいろなサイトを探ってはいるものの、テストの基礎に関して紹介しているサイトが
発見できなかったので、皆様のアドバイスを聞きたく存じます。

もし、単体テストの方法について説明いただける、もしくはこのサイト見れば一通りできる等
ございましたら、よろしくお願い致します。
Best Answer chosen by k_ogawa
Taiki YoshikawaTaiki Yoshikawa
テストクラスは実際に処理を動かしてみて想定通りに動作するかをテストするためのものなので、
それほど難しく考えなくて大丈夫です。

例えばこんな感じのクラスを作ったとします。
public class SampleClassController {
    
    public String searchKey {get; set;}
    public List<Account> accounts {get; set;}
    
    /**
     * コンストラクタ
     */
    public SampleClassController() {
        this.searchKey = 'ABC';
    }
    
    /**
     * 検索処理
     */
    public void doSearch() {
        this.accounts = [SELECT Id FROM Account WHERE Name =: this.searchKey];
    }
}

この処理のコンストラクタをテストしたい場合は次の書き方でテストができます。
static testMethod void SampleClassControllerTest() {

    SampleClassController cls = new SampleClassController();

}

これだけでもテストを実行したときにカバーされていることが確認できます。ただし、これだけではコンストラクタの処理を実行時にシステムエラーが発生していないことしか確認できていません。実際にコンストラクタ内の処理を実行して想定どおりの値がセットされているかは「System.assertEquals」を使って確認します。この関数は指定した引数1と引数2の値が「不一致」だった場合にエラーにします。この関数で判定してエラーが発生しなければ想定どおりの処理になっていることを確認できます。
static testMethod void SampleClassControllerTest() {

    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
}

これでコンストラクタの処理を実行した際に変数「searchKey」に「ABC」の値がセットされていることを確認できます。

上のテストが成功することを確認できたら、コンストラクタでセットしている値を別の値にしてテストしてみてください。次のようにエラーになることを確認できると思います。

User-added image

コンストラクタでセットする値を'A'に変更してテストした結果です。System.assertEqualsで想定している結果は'ABC'のためエラーとなります。
テストクラスはこのように処理の結果が想定通りか確認していくためのものです。サンプルは値を直接指定していますが、実際にはクエリを実行した結果をセットしたり、URLパラメータから取得した値をセットするケースがでてくると思います。そのときに条件どおりに動作しているかテストクラスで確認できます。


コンストラクタのテストは上記の方法でできると思います。続いて検索処理を行っているdoSearch()のテストについてです。このような処理のテストは次の書き方でテストすることができます。
static testMethod void doSearchTest1() {

    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 取得結果0件
    System.assertEquals(cls.accounts.isEmpty(), true);
}

これで検索処理の結果が0件だったことをテストできます。テストクラス内では基本的にSalesforce組織内に登録されている既存のデータにはアクセスできないようになっています。そのため、1件以上取得する処理をテストしたい場合は、テストクラス内でテストデータを作成する必要があります。


処理の例です。
static testMethod void doSearchTest2() {
        
    // 取引先のテストデータ作成
    Account account = new Account(Name = 'ABC');
    insert account;
    
    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 1件取得
    System.assertEquals(cls.accounts.size(), 1);
}

このようにテストデータを作成することでテストクラス内でSalesforceのレコードにアクセスする処理をテストすることができます。


テストクラスを作成する際に便利なのが「Test.startTest()」と「Test.stopTest()」です。これを使用することでテストデータ作成処理で消費したDML発行数をリセットすることができます。テストクラス内で一度しか使用できませんが、テストデータ作成とテストの実行を分けて考えることができます。

使用例です。
static testMethod void doSearchTest2() {
        
    // 取引先のテストデータ作成
    Account account = new Account(Name = 'ABC');
    insert account;

    Test.startTest();
    
    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 1件取得
    System.assertEquals(cls.accounts.size(), 1);
    
    Test.stopTest();
}


テストクラスの基本的な考え方はこんな感じだと思います。


All Answers

Taiki YoshikawaTaiki Yoshikawa
テストクラスについてですが、こちらのサイトが参考になると思います。

Apexコードテストメソッドの概要
https://developer.salesforce.com/page/JP:An_Introduction_to_Apex_Code_Test_Methods

トリガのテストメソッドの作成方法について
https://help.salesforce.com/apex/HTViewSolution?id=000004325&language=ja (https://help.salesforce.com/apex/HTViewSolution?id=000004325&language=ja)
k_ogawak_ogawa
Taiki 様
いつもありがとうございます。

やはりこちらのサイトくらいしかないですか。。。
こちらのサイトにはたどり着いたのですが、まったく理解できませんでした。


Taiki YoshikawaTaiki Yoshikawa
テストクラスは実際に処理を動かしてみて想定通りに動作するかをテストするためのものなので、
それほど難しく考えなくて大丈夫です。

例えばこんな感じのクラスを作ったとします。
public class SampleClassController {
    
    public String searchKey {get; set;}
    public List<Account> accounts {get; set;}
    
    /**
     * コンストラクタ
     */
    public SampleClassController() {
        this.searchKey = 'ABC';
    }
    
    /**
     * 検索処理
     */
    public void doSearch() {
        this.accounts = [SELECT Id FROM Account WHERE Name =: this.searchKey];
    }
}

この処理のコンストラクタをテストしたい場合は次の書き方でテストができます。
static testMethod void SampleClassControllerTest() {

    SampleClassController cls = new SampleClassController();

}

これだけでもテストを実行したときにカバーされていることが確認できます。ただし、これだけではコンストラクタの処理を実行時にシステムエラーが発生していないことしか確認できていません。実際にコンストラクタ内の処理を実行して想定どおりの値がセットされているかは「System.assertEquals」を使って確認します。この関数は指定した引数1と引数2の値が「不一致」だった場合にエラーにします。この関数で判定してエラーが発生しなければ想定どおりの処理になっていることを確認できます。
static testMethod void SampleClassControllerTest() {

    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
}

これでコンストラクタの処理を実行した際に変数「searchKey」に「ABC」の値がセットされていることを確認できます。

上のテストが成功することを確認できたら、コンストラクタでセットしている値を別の値にしてテストしてみてください。次のようにエラーになることを確認できると思います。

User-added image

コンストラクタでセットする値を'A'に変更してテストした結果です。System.assertEqualsで想定している結果は'ABC'のためエラーとなります。
テストクラスはこのように処理の結果が想定通りか確認していくためのものです。サンプルは値を直接指定していますが、実際にはクエリを実行した結果をセットしたり、URLパラメータから取得した値をセットするケースがでてくると思います。そのときに条件どおりに動作しているかテストクラスで確認できます。


コンストラクタのテストは上記の方法でできると思います。続いて検索処理を行っているdoSearch()のテストについてです。このような処理のテストは次の書き方でテストすることができます。
static testMethod void doSearchTest1() {

    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 取得結果0件
    System.assertEquals(cls.accounts.isEmpty(), true);
}

これで検索処理の結果が0件だったことをテストできます。テストクラス内では基本的にSalesforce組織内に登録されている既存のデータにはアクセスできないようになっています。そのため、1件以上取得する処理をテストしたい場合は、テストクラス内でテストデータを作成する必要があります。


処理の例です。
static testMethod void doSearchTest2() {
        
    // 取引先のテストデータ作成
    Account account = new Account(Name = 'ABC');
    insert account;
    
    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 1件取得
    System.assertEquals(cls.accounts.size(), 1);
}

このようにテストデータを作成することでテストクラス内でSalesforceのレコードにアクセスする処理をテストすることができます。


テストクラスを作成する際に便利なのが「Test.startTest()」と「Test.stopTest()」です。これを使用することでテストデータ作成処理で消費したDML発行数をリセットすることができます。テストクラス内で一度しか使用できませんが、テストデータ作成とテストの実行を分けて考えることができます。

使用例です。
static testMethod void doSearchTest2() {
        
    // 取引先のテストデータ作成
    Account account = new Account(Name = 'ABC');
    insert account;

    Test.startTest();
    
    SampleClassController cls = new SampleClassController();
    System.assertEquals(cls.searchKey, 'ABC');
    
    // 検索処理
    cls.doSearch();
    // 1件取得
    System.assertEquals(cls.accounts.size(), 1);
    
    Test.stopTest();
}


テストクラスの基本的な考え方はこんな感じだと思います。


This was selected as the best answer
Taiki YoshikawaTaiki Yoshikawa
上記のテストクラスのサンプルまとめはこちらです。
@isTest
private class SampleClassControllerTest {
	
    /**
     * コンストラクタのテスト
     */
    static testMethod void SampleClassControllerTest() {
        Test.startTest();
        
        SampleClassController cls = new SampleClassController();
        System.assertEquals(cls.searchKey, 'ABC');
        
        Test.stopTest();
    }
    
    /**
     * 検索処理のテスト
     * テストデータなし
     */
    static testMethod void doSearchTest1() {
        Test.startTest();
        
        SampleClassController cls = new SampleClassController();
        System.assertEquals(cls.searchKey, 'ABC');
        
        // 検索処理
        cls.doSearch();
        // 取得結果0件
        System.assertEquals(cls.accounts.isEmpty(), true);
        
        Test.stopTest();
    }
    
    /**
     * 検索処理のテスト
     * テストデータあり
     */
    static testMethod void doSearchTest2() {
        
        Account account = new Account(Name = 'ABC');
        insert account;
        
        Test.startTest();
        
        SampleClassController cls = new SampleClassController();
        System.assertEquals(cls.searchKey, 'ABC');
        
        // 検索処理
        cls.doSearch();
        // 1件取得
        System.assertEquals(cls.accounts.size(), 1);
        
        Test.stopTest();
    }
}


Taiki YoshikawaTaiki Yoshikawa
テストデータを大量件数用意して検索処理を実行して正しく動作するかのテストや、検索条件にセットされた値が存在しない状態で処理を実行するなど処理に応じてテストケースを増やしていけばいいと思います。

そのあたりのことが上で紹介した「Apexコードテストメソッドの概要」のページにまとめられています。
k_ogawak_ogawa
Taiki 様
詳細な説明ありがとうございます。

まだテストがすべて完了したわけではありませんが、複雑な処理でなければ、
コンストラクタと変数の確認だけでかなりのカバー率を稼げました。

ただコンストラクタを単純に宣言するだけではうまくいかないクラスもありまだまだてこずっています。
そこに関しては応用だと思うので、当初の質問に対しては一旦クローズとさせて頂きます。

本当にありがとうございました。
またよろしくお願い致します。