人に伝える機会も増えてきたのでまとめます。
最後に悪い例と良い例のサンプルコードも載せています(*'ω'*)
testMethodではなくisTestを使用する
Salesforce では、testMethod ではなく @IsTest を使用することをベストプラクティスとして推奨しています。testMethod キーワードは今後のリリースで廃止される可能性があります。
古いソースコード見ると残っていたりします。
新たに作られたテストクラスでtestMethod書いてたら、、( ^)o(^ )おこ
isTest(SeeAllData=True)をむやみやたらに使用しない
参考:isTest(SeeAllData=True) アノテーションの使用
原則使わない。がいいと思います。特別な場合ってほんとに極稀です。
TestSetupアノテーションを使用する
テストメソッドで毎度毎度データ作っていると実行時間がかなり長くなりますので
TestSetupを使用することをおすすめします。以下の記事で実証しています!
System.runAs()を使用する
指定しない場合はテストを実行したユーザ(システム管理者)になるので、実際に運用するプロファイルのユーザを指定してあげます。
このユーザもtestSetupで作ってあげるといいでしょう。
1テストメソッド1テストとする
1メソッド内で色々呼び出して、何のテストをしているのか分からないのをたまに見かけますが
それはナンセンスかと思います。あくまで単体テストなので。
startTestとstopTestを使用する
参考:Limits、startTest、および stopTest の使用
テストメソッドの最初と最後に入れているのをたまに見かけますが
適当に入れたことバレるのでやめたほうがいいですw
※これは具体的に解説したいので後日記事にする予定
アサーションを入れる
参考:System クラス
明確かつ計画的な Apex アサーションの記述
Assert Class
アサーションがないのはテストしてないも同義ではないかと思います。(過激発言)
- 戻り値があればそれを
- InsertやDeleteがあるならレコード数を
- updateがあるなら更新した項目を
くらいは最低限見るべきかと思います。
最近はWinter'23でリリースされたAssertクラスを個人的には使っています。
VSCodeでデバッグする
System.debug()を使うのもいいですが、デバッガあるので使いましょ。
設定方法は以下にまとめています。
サンプルコード
※両例ともに当記事の指摘箇所についてのみコメントしています。
@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);
// アサーションがない
}
}
@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);
}
}
てな感じでテストクラスは適当に書くこともできますが
色々気にすると興味深いですよね(*'ω'*)