blog
ブログ

Salesforce技術ブログ(開発者コンソールの匿名実行Apexツールでテストカバー率一覧を取得してみる)

こんにちは!ドライ市来です!

皆さんビールは何派でしょうか?
私はもちろんアサヒのスーパードライ派です!(クリアアサヒではありません!)

開発者コンソールの匿名実行Apexツールは活用してますか?

そもそも、開発者コンソールの匿名実行Apexツールとはなんだ!?って方、
下記ヘルプを参照してください。m(_ _)m
https://help.salesforce.com/apex/HTViewHelpDoc?id=code_dev_console_execute_anonymous.htm&language=ja

本題ですが、
このツールを使っていろいろとやってみようと思います。

その第1弾として、クラスとトリガのテストカバー率を取得するApexコードを書いてみました!
※テストクラスの実行はしません。現在のカバー率を取得します。

まず、なぜこのコードを書いたかというと、ご存知の方もいらっしゃると思いますが、
Winter’14のリリースでApexクラス一覧からクラスのカバー率の列が削除されましたよね。

リリースノート抜粋_コードカバー率について

https://resources.docs.salesforce.com/186/latest/ja-jp/sfdc/pdf/salesforce_winter14_release_notes.pdf
↑306ページ

なんで削除されたんだ?と思った方も多いのではないでしょうか。(たぶん…)

ということで、どうにか一覧表として取得できないかなぁと思い、書いてみました。

開発者コンソールの[Debug]->[Open Execute Anonymous Window]で匿名実行Apexツールを開き、
下記コードを貼り付けて[Execute]ボタンをクリックすると、
ドキュメントの「私の個人ドキュメント」フォルダにクラスとトリガのカバー率一覧のファイルが
XMLスプレッドシート形式で保存されます!!


// ClassとTriggerのカバー率を取得するクエリ
String queryStr = 'Select+id,ApexClassorTriggerId,ApexClassorTrigger.Name,NumLinesCovered,NumLinesUncovered+from+ApexCodeCoverageAggregate+order+by+ApexClassorTrigger.Name';

HttpRequest req = new HttpRequest();
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v37.0/tooling/query/?q='+queryStr);
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);

Map<String, Object> objMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());

List<Object> objList = (List<Object>)objMap.get('records');

// クラス一覧ワークシート
String clsListWS = '';
// トリガ一覧ワークシート
String trgListWS = '';

clsListWS += '<Worksheet ss_Name="クラス一覧">rn' +
               '<Table ss_ExpandedColumnCount="7" x_FullColumns="1" x_FullRows="1" ss_DefaultColumnWidth="54" ss_DefaultRowHeight="13.5">rn' +
                 '<Column ss_AutoFitWidth="0" ss_Width="21.75"/>rn' +
                   '<Row>rn' +
                     '<Cell><Data ss_Type="String">クラス一覧</Data></Cell>rn' +
                   '</Row>rn' +
                   '<Row>rn' +
                     '<Cell><Data ss_Type="String">No</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Id</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Name</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">NumLinesCovered</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">NumLinesUncovered</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">TotalNumLines</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Coverage</Data></Cell>rn' +
                   '</Row>rn';


trgListWS += '<Worksheet ss_Name="トリガ一覧">rn' +
               '<Table ss_ExpandedColumnCount="7" x_FullColumns="1" x_FullRows="1" ss_DefaultColumnWidth="54" ss_DefaultRowHeight="13.5">rn' +
                 '<Column ss_AutoFitWidth="0" ss_Width="21.75"/>rn' +
                   '<Row>rn' +
                     '<Cell><Data ss_Type="String">トリガ一覧</Data></Cell>rn' +
                   '</Row>rn' +
                   '<Row>rn' +
                     '<Cell><Data ss_Type="String">No</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Id</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Name</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">NumLinesCovered</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">NumLinesUncovered</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">TotalNumLines</Data></Cell>rn' +
                     '<Cell><Data ss_Type="String">Coverage</Data></Cell>rn' +
                   '</Row>rn';

Integer clsCnt = 1;
Integer trgCnt = 1;
for(Object obj : objList){
    Map<String, Object> codeCoverageObj = (Map<String, Object>)obj;
    Map<String, Object> clsOrTrgObj = (Map<String, Object>)codeCoverageObj.get('ApexClassOrTrigger');
    
    Double numLinesCovered = Double.valueOf(codeCoverageObj.get('NumLinesCovered'));
    Double totalNumLines = Double.valueOf(codeCoverageObj.get('NumLinesCovered')) + Double.valueOf(codeCoverageObj.get('NumLinesUncovered'));
    
    // クラス単体のカバー率を計算
    Decimal coverage = 0;
    if(numLinesCovered != 0){
        coverage = (numLinesCovered / totalNumLines) * 100;
        coverage = coverage.setScale(0, System.RoundingMode.HALF_UP);
    }
    
    String clsOrTrgId = String.valueOf(codeCoverageObj.get('ApexClassOrTriggerId'));
    
    // ClassのIdのPrefix
    if(clsOrTrgId.startsWith('01p')){
        clsListWS += '<Row>rn' +
                       '<Cell><Data ss_Type="Number">' + clsCnt + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + clsOrTrgId + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + String.valueOf(clsOrTrgObj.get('Name')) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(numLinesCovered) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(codeCoverageObj.get('NumLinesUncovered')) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(totalNumLines) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + coverage + '%' + '</Data></Cell>rn' +
                     '</Row>rn';
        
        clsCnt++;
    }
    // TriggerのIdのPrefix
    else if(clsOrTrgId.startsWith('01q')){
        trgListWS += '<Row>rn' +
                       '<Cell><Data ss_Type="Number">' + trgCnt + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + clsOrTrgId + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + String.valueOf(clsOrTrgObj.get('Name')) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(numLinesCovered) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(codeCoverageObj.get('NumLinesUncovered')) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="Number">' + String.valueOf(totalNumLines) + '</Data></Cell>rn' +
                       '<Cell><Data ss_Type="String">' + coverage + '%' + '</Data></Cell>rn' +
                     '</Row>rn';
        
        trgCnt++;
    }
}

clsListWS += '</Table>rn' +
           '</Worksheet>rn';

trgListWS += '</Table>rn' +
           '</Worksheet>rn';

String workBook = '<?xml version="1.0"?>rn' +
                  '<?mso-application progid="Excel.Sheet"?>rn' +
                  '<Workbook     >rn' +
                  clsListWS +
                  trgListWS +
                  '</Workbook>';

Document doc = new Document();
String timeStamp = DateTime.now().format('yyyyMMddHHmmss');
doc.Name = 'TestCoverage_' + timeStamp + '.xml';
doc.DeveloperName = 'TestCoverage_' + timeStamp + '_xml';
doc.FolderId = UserInfo.getUserId();
doc.Body = Blob.valueOf(workBook);
doc.type = 'xml';
insert doc;

※このコードを実行前に「リモートサイトの設定」が必要となります。
[設定]->[セキュリティのコントロール]->[リモートサイトの設定]で下記URLを設定しておいてください。
https://【ログイン環境のインスタンス】.salesforce.com

出力されるファイルはこんな感じです↓

カバー率_出力ファイル

ぜひ活用してみてください!

contact

ご相談・ご質問等ございましたら、お気軽にお問い合わせください。

翻訳