ApexEloquent v2 の新機能について

8 min read
ApexEloquentv2Release NotesScribeMockEntryTestingSalesforceApex
ApexEloquent v2 で増えた機能を一気に紹介します。`Scribe.of()` の導入、MockEntry の `alias` や `template`、IEloquent の rawSOQL 受け入れなど、テストの書き味がぐっと楽になっています。

先日、無事にApexEloquentのv2.0.0をリリースしました!

(急遽追加したい機能があったため、すでにv2.0.xに進んでいますが)

今回はその中で追加した機能にフォーカスを当てて紹介していきたいと思います!

エラーメッセージの統一

今までは標準のQueryExceptionを投げていたのですが、これを変更し、ApexEloquentExceptionを投げるようにしました。

このApexEloquentException

  • どこで起きたか
  • どんなエラーか
  • 理由は何か
  • 次に取るべきアクションは何か

をひとまとめにログに出してくれるので、人が読んでももちろんわかりやすいですし、AIが実行しても次にどうすればいいのかが明らかなのでApexEloquentの使い方により迷いにくくなりました!

Scribe

初期用 of メソッドの新設

今まで、Scribeの起点は

APEX
Scribe oppScribe = Scribe.source(Opportunity.getSObjectType());

でしたが、ちょっと長くてみづらかったのでApexBlueprintと合わせて

APEX
Scribe oppScribe = Scribe.of(Opportunity.class);

で宣言できるようにしました。特にネストしたクエリでは可読性が上がったと思います!


子リレーションの動的解決の高速化

Scribeで子リレーションを書く時

APEX
Scribe scribe = Scribe.of(Account.class)
    .field('Name')
    .withChildren(
        Scribe.asChild(Contact.class)
            .fields(new List<String>{'Id', 'Email'})
            .whereNotNull('Email')
    );

と書くと

SOQL
SELECT name, (SELECT id, email FROM Contacts WHERE Email != NULL) FROM Account

というクエリが発行されます。

asChild(Contact.class) で宣言された部分が、親のAccountをみながらどんなリレーション名かを動的に解決しているんですが

この解決方法に二分探索を用いるようにしました。

具体的な内容はまたブログで書きますが、親が持つ子のオブジェクト名がUnicodeの順に並んでいるので、ここで二分探索を仕込みました。

重い処理であるgetDescribeの呼ぶ回数がグッと減る実装になったため、テスト時間がv2になり減りました!

この二分探索は組織が大きくなってリレーションが増えても影響が少ないため、テストの高速化により貢献できる機能かと思います

IEntry

getChildrenByRelationName を getChildren に統合

Scribe.relationName('xxx__r') を指定したとき、子の取得は getChildrenByRelationName('xxx__r') を使う必要がある (getChildren('xxx__r') ではない) のですが、claudeさんがこれに引っかかっていたそうなので

getChildren('xxx__r') で取れるようにしました。

AIフレンドリー!

MockEntry

モックデータに関する機能をかなり増やしました。テストを書くのがもっと楽に、楽しくなるはずです!


初期化用 of メソッドの新設

Scribeと同様に

APEX
MockEntry.of(Opportunity.class)

と書けるようになりました!スッキリしますね


SELECT忘れ検知機能をOFFにする機能

ScribefieldfieldsでSELECTする項目を追加しますが、そこに入れていない項目にアクセスした時、モックデータでもエラーで落とすという「SELECT忘れ検知機能」をOFFにする機能ですね

具体的には

APEX
MockEntry.withoutFieldValidation()

のように、withoutFieldValidation()メソッドをチェーンに入れるだけです!

きっとどこかで役に立つはずです!


asAggregateResultで集計系のモックであることを表現する

MockEntryはAggregateResultもモックできますが、宣言する際にコツがあったので

APEX
MockEntry.asAggregateResult().set('count', 10);

のように集計のモックであることをわかりやすく表現できるようになりました!


フィールドの設定を set メソッドで行う

今まで add メソッドでフィールドの設定などをしていましたが、set を追加しました。

addset で挙動は同じですが、v3でaddは削除予定です。

自分で書いていて、フィールドを設定する時についset を書いてしまうことから、addの方が一般的ではないかもと思いsetを追加しました!


aliasと、aliasを使った抜き出し

次のようにidや値をセットした時、その値を抜き出す手段がありませんでした。

APEX
MockEntry oppEntry = MockEntry.of(Opportunity.class).autoId(1).set('Name', 'TestOpp');

しかしテストを書いているときに、↑で作ったoppEntryのIdが欲しいなぁというケースが結構ありました。

そこで、

  • alias
  • getByAlias
  • getAliasId

というAPIを追加しました!使い方は

  • MockEntryにaliasでエイリアスを付与
  • getByAliasでそのMockEntryを取り出す(親子も自動的にたどるよ!)
  • Idだけ欲しい時はgetAliasId で取り出す!
APEX
MockEntry oppEntry = MockEntry.of(Opportunity.class)
    .alias('opp')       // エイリアスとしてoppを付与
    .autoId(1)
    .set('Name', 'TestOpp');
  
// Idを直接取り出す
Id mockOppId = oppEntry.getAliasId('opp');
 
// MockEntryを取り出す
MockEntry mockOpp = oppEntry.getByAlias('opp');
String mockOppName = mockOpp.getName();

これ、Assertを書く時にすごく便利です!ぜひ活用ください!


template メソッドの追加

テストデータのセットアップをするときに、同じ構成がたくさん出る時はtemplateメソッドの出番です!

APEX
private static Map<String, Object> getTemplate() {
    return new Map<String, Object>{
        'Name' => 'testOpp'
        ...
    }
}

こんな感じで、よく使う項目をまとめて返すメソッドを用意しておいて、テストクラスで

APEX
MockEntry oppEntry = MockEntry.of(Opportunity.class)
    .template(xxxTest.getTemplate())
    .set(...)

のようにすれば、共通セットアップが完了です!うまくDRYをしましょう!

IEloquent

getAggregateをgetメソッドに統合

とにかくわかりづらく、私もわからなかったのでいっそ統合しました!


rawSOQLを受け入れるように

Scribeを強要していたのを、文字列のrawSOQLを受け入れるようになりました!

APEX
String soql = 'SELECT Id, Name FROM Opportunity LIMIT 10';
 
List<IEntry> oppEntries = (new Eloquent()).rawSoql(soql);

なお、この時は「SELECT忘れ検知機能」が強制的にOFFになりますのでご注意ください!

MockEloquent

failOnXxx 系のメソッドの追加

MockEloquentは、getやdoUpdateとかで意図的にエラーを投げられるようになっていますが、コンストラクタの第二引数にエラーを入れるという形で非常にわかりにくかったため

  • failOnGet
  • failOnFirst
  • failOnDoInsert
  • failOnDoUpsert

... などの failOnXxx メソッドを追加しました!

APEX
IEloquent eloquent = (new MockEloquent(mockEntry)).failOnGet();

このように定義して、DIするとgetメソッドが呼ばれたタイミングでエラーが投げられます!

これでキャッチブロックやリトライの検証がさらにやりやすくなりましたね!


deletedCountを追加

doDeleteした時に渡されたレコードを数を記録するようになりました。これで

APEX
// プロダクションコード
this.eloquent.doDelete(deleteTargets);
 
// テストクラス
Assert.areEqual(10, this.eloquent.deletedCount);

という、デリートに関わるアサートが可能になりました!