ちょっと面倒な価格表のテスト

今回もまたテストクラスの分野から。
価格表を扱う商談商品のテストクラスのところがやや複雑なのでやり方をメモ。

スポンサーリンク

テストデータの作成が面倒くさい

商談商品を含めたテストデータを作成する場合は、いくつかの手順を踏む必要があります。
手順の説明の前にまずは、構成を整理しましょう。

こちらが商談商品を含む関連エンティティのER図となります。

商談商品まわりのオブジェクト構成

つまり、テストデータで商談商品を作成したい時は、商談商品が参照している商品を作成する必要があり、商品を作成するには、商品が参照している価格表エントリを作成する必要があったり等々、数珠つなぎ的に価格表までのデータを作成しなければいけません。

テストデータ作成の手順はこのようになります。

  1. 取引先を作成
  2. 商談を作成
  3. 商品を作成
  4. 価格表を作成
  5. 価格表エントリを作成
  6. 商談商品を作成

それだけであればいいのですが、もう一つ厄介なのが、4の価格表のテストデータの作成。
実際にテストデータ内で作成しようとすると、こういうエラーが。

System.DmlException: Insert failed. First exception on row 0; first error: STANDARD_PRICE_NOT_DEFINED, No standard price defined for this product: []

標準の価格表が定義されていないというエラーなのですが、標準の価格表はテストクラス内で作成ができないので、実データから引っ張ってくる必要があります。

そのために、テストクラス(メソッド)にseeAllDataをtrueにしないと価格表を含めた商談商品のテストデータの作成が行えません。

以上を踏まえて取引先から商談商品までを作成したテストクラスがこちら。

    @IsTest(seeAllData=true)
    private static void oppTesttWithAllData() {
        // 1.取引先の作成
        Account a = new Account(Name = 'hoge');
        insert a;
        
        // 2.商談の作成
        Opportunity o = new Opportunity(Name = 'test', AccountId = a.Id);
        o.CloseDate = Date.today().addDays(10);
        o.StageName = 'Prospecting ';
        insert o;
        
        // 3.商品の作成
        Product2 p = new Product2(Name = 'p1');
        insert p;
        
        // 4.価格表の取得
        // ここを取得するために、seeAllData=trueにしないといけない
        PriceBook2 pb = [Select Id From PriceBook2 Where IsStandard = true Limit 1];
        
        // 5.価格表エントリの作成
        PriceBookEntry pbe = new PriceBookEntry();
        pbe.Pricebook2Id = pb.Id;
        pbe.Product2Id  = p.Id;
        pbe.UnitPrice  = 1000;
        insert pbe;
        
        // 6.商談商品の作成
        OpportunityLineItem ol = new OpportunityLineItem();
        ol.OpportunityId = o.Id;
        ol.PricebookEntryId = pbe.Id;
        
        Test.startTest();
        OpportunityUtil.OpportunityLineItemSum(o.Id);
        Test.stopTest();
    }

Test.getStandardPricebookIdを使用する

実は、前述したテストクラスの記載方法は昔話で、今はTest.getStandardPricebookIdを使用することで、標準価格表のIDを取得することができます。
これによって、最新のAPIバージョンでは、seeAllDataをtrueにする必要はなくなっています。

書き換えたテストクラスはこのようになります。

    @IsTest
    private static void oppTesttWithoutAllData() {
        // 1.取引先の作成
        Account a = new Account(Name = 'hoge');
        insert a;
        
        // 2.商談の作成
        Opportunity o = new Opportunity(Name = 'test', AccountId = a.Id);
        o.CloseDate = Date.today().addDays(10);
        o.StageName = 'Prospecting ';
        insert o;
        
        // 3.商品の作成
        Product2 p = new Product2(Name = 'p1');
        insert p;
        
        // 4.価格表の取得
        // Test.getStandardPricebookId使用でこの処理は不要となる
        // PriceBook2 pb = [Select Id From PriceBook2 Where IsStandard = true Limit 1];
        
        // 5.価格表エントリの作成
        PriceBookEntry pbe = new PriceBookEntry();
        pbe.Pricebook2Id = Test.getStandardPricebookId();
        pbe.Product2Id  = p.Id;
        pbe.UnitPrice  = 1000;
        insert pbe;
        
        // 6.商談商品の作成
        OpportunityLineItem ol = new OpportunityLineItem();
        ol.OpportunityId = o.Id;
        ol.PricebookEntryId = pbe.Id;
        
        Test.startTest();
        OpportunityUtil.OpportunityLineItemSum(o.Id);
        Test.stopTest();
    }

ポイントは価格表エントリ作成のところで、価格表への参照項目をTest.getStandardPricebookId()を使用することで、価格表を作成することなく、価格表エントリが作成できます。
もちろん、seeAllDataはfalseなので、実データに依存することはありません。

これで、すごい面倒いから、ちょっと面倒いくらいになったのではないでしょうか(笑)。

まとめ

  • 価格表のテストデータ作成にはTest.getStandardPricebookIdを使用する
  • Summer14以降はseeAllData=falseで標準価格表のテストが可能に

コメント