【Salesforce】テストクラスのカバレッジを削除する
Salesforceでテストクラスを作成する際に、TestUtilなんてクラスを作ったことはないでしょうか。
クラスに@IsTestアノテーションを付与し、テストクラスを実行するためのユーティリティとして使用することが出来ますね。
通常であれば@IsTestが付与されているクラスはコードカバー率の対象に含まれません。
しかし、内部クラスや静的でないメソッドが含まれる場合に対象とされてしまうことがあるようです。
https://help.salesforce.com/articleView?id=000188441&language=ja&type=1
そんなときは対象となってしまったTestUtilクラスを修正すれば良いのですが、それでもコードカバー率の対象から外れないことがあります。
本番へリリースする際には対象外となるので影響がないとしても、このままでは正しいコードカバー率を計算することが出来ません。
というわけで、コードカバー率の対象となってしまったTestUtilを対象外にします。
一番簡単な方法は、開発者コンソールのクエリエディタを使用する方法でしょうか。
開発者コンソールを開き、「Query Editor」タブで「Use Tooling API」にチェックを入れます。
そして、以下のクエリを実行します。
SELECT Id, ApexClassOrTrigger.Name, NumLinesCovered, NumLinesUncovered FROM ApexCodeCoverageAggregate
これにより、コードカバー率を表すレコードが表示されるかと思います。
@IsTestが付与されているのに対象となってしまっているクラスを選択し、「Delete Row」で削除します。
レコードを削除したら、全テストを実行します。
全テストの結果、TestUtilクラスがコードカバー率の対象から外れていれば成功です。
コードカバー率の対象から外れていない場合、TestUtilクラスの中に対象となるコードがあるかもしれません。
ちなみに、私はクエリエディタから実行できることを知らなかったため、REST APIを使って削除しました。
上のURLではワークベンチを使用して削除していますね。
なんだか悔しいので、REST APIを使用した方法を記述しておきます。
開発者コンソールからREST APIを呼び出しています。
HttpRequest req; HttpResponse res; Http h; String endpoint; // エンドポイントを設定する endpoint = ''; endpoint += URL.getSalesforceBaseUrl().toExternalForm(); endpoint += '/services/data/v41.0/tooling/query/?q='; endpoint += 'SELECT+Id,+ApexClassOrTriggerId,+ApexClassOrTrigger.Name,+NumLinesCovered,+NumLinesUncovered+FROM+ApexCodeCoverageAggregate+'; endpoint += 'WHERE+ApexClassOrTrigger.Name+=+\'TestUtil\''; // HTTPリクエストを作成する req = new HttpRequest(); req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); req.setHeader('Content-Type', 'application/json'); req.setEndpoint(endpoint); req.setMethod('GET'); // HTTPリクエストを送信する h = new Http(); res = h.send(req); system.debug(res); system.debug(res.getBody());
TestUtilクラスのカバレッジを取得しています。
こんなのが返ってきました。
{ "size":1, "totalSize":1, "done":true, "queryLocator":null, "entityTypeName":"ApexCodeCoverageAggregate", "records": [ { "attributes": { "type":"ApexCodeCoverageAggregate", "url":"/services/data/v41.0/tooling/sobjects/ApexCodeCoverageAggregate/7150V000000sfZoQAI" }, "Id":"7150V000000sfZoQAI", "ApexClassOrTriggerId":"01p0V000003STM7QAO", "ApexClassOrTrigger": { "attributes": { "type":"Name", "url":"/services/data/v41.0/tooling/sobjects/ApexClass/01p0V000003STM7QAO" }, "Name":"TestUtil" }, "NumLinesCovered":0, "NumLinesUncovered":4 } ] }
“type”:”ApexCodeCoverageAggregate”がTestUtilクラスのカバー率を表すレコードです。
これを削除してやれば良いわけですね。
エンドポイントも取得されているので楽です。
REST APIのDELETEメソッドを呼び出すことで削除することが出来ます。
HttpRequest req; HttpResponse res; Http h; String endpoint; // エンドポイントを設定する endpoint = ''; endpoint += URL.getSalesforceBaseUrl().toExternalForm(); endpoint += '/services/data/v41.0/tooling/sobjects/ApexCodeCoverageAggregate/7150V000000sfZoQAI'; // HTTPリクエストを作成する req = new HttpRequest(); req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); req.setHeader('Content-Type', 'application/json'); req.setEndpoint(endpoint); req.setMethod('DELETE'); // HTTPリクエストを送信する h = new Http(); res = h.send(req); system.debug(res); system.debug(res.getBody());
ここで間違えてApexClassを削除するとクラスが消えてしまうので注意してください。
恥ずかしながら、自分のDeveloper Edition環境で実験するときにやらかして吹き飛んでしまいました。
即興で作ったクラスなので良かったですけども。
Compositeを使えば1発で削除することが出来ますが、ちょっと怖いですかね。
HttpRequest req; HttpResponse res; Http h; String query; String endpoint; String requestBody; Map<String, Object> compositeMap; List<Map<String, Object>> compositeRequestList; // クエリ用文字列を作成する query = ''; query += '/services/data/v41.0/tooling/query/?q='; query += 'SELECT+Id,+ApexClassOrTriggerId+FROM+ApexCodeCoverageAggregate+'; query += 'WHERE+ApexClassOrTrigger.Name+=+\'TestUtil\''; // リクエストリストを作成する compositeRequestList = new List<Map<String, Object>>(); // SOQL要求 compositeRequestList.add(new Map<String, Object>()); compositeRequestList[0].put('method', 'GET'); compositeRequestList[0].put('url', query); compositeRequestList[0].put('referenceId', 'record'); // DELETE compositeRequestList.add(new Map<String, Object>()); compositeRequestList[1].put('method', 'DELETE'); compositeRequestList[1].put('url', '/services/data/v41.0/tooling/sobjects/ApexCodeCoverageAggregate/@{record.records[0].Id}'); compositeRequestList[1].put('referenceId', 'deleteRecord'); // リクエストBodyを作成する compositeMap = new Map<String, Object>(); compositeMap.put('allOrNone', true); compositeMap.put('compositeRequest', compositeRequestList); requestBody = JSON.serialize(compositeMap); // エンドポイントを設定する endpoint = ''; endpoint += URL.getSalesforceBaseUrl().toExternalForm(); endpoint += '/services/data/v41.0/composite'; // HTTPリクエストを作成する req = new HttpRequest(); req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); req.setHeader('Content-Type', 'application/json'); req.setEndpoint(endpoint); req.setBody(requestBody); req.setMethod('POST'); system.debug(req); system.debug(req.getBody()); // HTTPリクエストを送信する h = new Http(); res = h.send(req); system.debug(res); system.debug(res.getBody());
TestUtilクラスのカバレッジレコードを取得し、DELETEしています。
成功時に帰ってきたJSONはこんな感じです。
{ "compositeResponse": [ { "body": { "totalSize":1, "done":true, "records": [ { "attributes": { "type":"ApexCodeCoverageAggregate", "url":"/services/data/v41.0/sobjects/ApexCodeCoverageAggregate/7150V000000sfa3QAA" } } ] }, "httpHeaders": { }, "httpStatusCode":200, "referenceId":"record" }, { "body":null, "httpHeaders": { }, "httpStatusCode":204, "referenceId":"deleteRecord" } ] }
エラーメッセージが出ていなければ削除に成功しているはずです。
No comments.