4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数のテストバンドルに分かれたXCTestのカバレッジをマージする

Last updated at Posted at 2022-02-27

概要

この記事では、XCTestのカバレッジデータをコマンドライン上で操作する方法に関して解説します。
カバレッジデータを操作するには専用のデータ(.xcresult)とコマンド操作(xccov, xcresulttools)が必要となります。
また、具体的な応用事例として 「複数に分かれたテストバンドルのカバレッジデータをマージして総カバレッジを出す」 というのを行います。

カバレッジの解析に必要な道具を揃える・理解する

まず初めに、カバレッジをコマンドラインで操作するために必要な道具について解説します。

.xcresultファイルを取得する

カバレッジデータを生成するにはXcodeでテストカバレッジ計測をオンにする必要があります。
まず、Scheme一覧のEdit SchemeからTestのタブを開いてチェックをつけます。

スクリーンショット 2022-02-26 23.06.53.png

その後Cmd+Uでテストを実行すると、テストカバレッジが生成されるようになります。
テストカバレッジはProject Navigatorの「Show Report Navigator」から確認できます。

スクリーンショット 2022-02-26 23.10.51.png

右クリックをして「Show in Finder」をクリックするとファイルがある場所に移ることができます。

スクリーンショット 2022-02-27 17.58.02.png

テストカバレッジのデータは.xcresultというパッケージで保存されており、このファイルはコマンドラインからの扱えます。

解析するには以下の2つのコマンドを使用します。
役割は似ていて結構同じことができるのですが、少しずつ違いがあるためどちらも使えるようになっておいた方がいいでしょう。

  • xccov
  • xcresulttool

2つともxcrunを経由して実行します。

xcresulttoolの使い方

ドキュメントは用意されていません(manページのみ)。何ができるかの詳細はhelpサブコマンドで調べていきます。

xcrun xcresulttool --help

helpサブコマンドを打つと使えるサブコマンド一覧が出力されるので、さらにそれらに対して--helpを実行して役割や使い方を調べていきます。

xcrun xcresulttool get --help

getに対して使用可能なオプションが見つかるのでそれらを使って実行しています。

xcrun xcresulttool get --path sample.xcresult --format json

このコマンドを実行するとjson形式でテストカバレッジのメタデータが出力されます。

xccov

ドキュメントは用意されていません(manページのみ)。何ができるかの詳細はhelpサブコマンドで調べていきます。

xcrun xccov help

helpサブコマンドを打つと使えるサブコマンドとそれらの使い方がわかるので、結果をもとに実行します

xcrun xccov view --archive --file-list export.xcresult

このコマンドはファイル単位でのカバレッジをリストで出力します。

.xcresultから.xccovarchiveと.xccovreportの生成

.xcresultを直接扱うこともできるのですが、より解析しやすくするため.xccovarchive.xccovreportの2つを生成します。.xccovarchiveにはファイルの各行に関する細かいカバレッジデータが含まれており、.xccovreportはメタデータを扱います。

作業は少し癖があって最初はとっつきにくいですが、慣れれば簡単です。

idを探し当てる

上述の2つを生成するためには.xcresult内にあるIDを取得する必要があります。

.xcresultはディレクトリで、その中にはInfo.plistと分割されたバイナリデータがあります。
バイナリデータはそのままでは読むことができないのでこれを読める形に復元します。

sample.xcresult
├── Data
│   ├── data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
│   ├── data.0~5jnweIyI7KJZYMCxddH5dlK1FAr0xdWnIQCNLIKNRHATSI_gxPVdo2kkhDEgt14ciE9NBe7UEem64nYPiiicIA==
│   ├── data.0~6ISB5n3exwjH0YZeXQqQ-AAavppAXZKqnIW5unTxqbGwhFic4mXBF_q4dYFN8D3p_li1EwkrJjPGFRE-l_oo7Q==
│   ├── data.0~8X6LHAW4WpROz2od-hssStrzKjuJooIzdqLoV4BY_u33BC-TNAqYiEuomvjlkWn982zIWbX2GmiCmbtEA_tNiw==
│   ├── data.0~DUsyWDayE2_xnv8PuhnvGOveEo-6e8Usv-j2DKz9ana8rR2JslxjMgR0jxOX9Iz2Y8BF9wQvxQlTzspJJv9w4g==
│   ├── refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
│   ├── refs.0~5jnweIyI7KJZYMCxddH5dlK1FAr0xdWnIQCNLIKNRHATSI_gxPVdo2kkhDEgt14ciE9NBe7UEem64nYPiiicIA==
│   ├── refs.0~6ISB5n3exwjH0YZeXQqQ-AAavppAXZKqnIW5unTxqbGwhFic4mXBF_q4dYFN8D3p_li1EwkrJjPGFRE-l_oo7Q==
│   ├── refs.0~8X6LHAW4WpROz2od-hssStrzKjuJooIzdqLoV4BY_u33BC-TNAqYiEuomvjlkWn982zIWbX2GmiCmbtEA_tNiw==
│   ├── refs.0~DUsyWDayE2_xnv8PuhnvGOveEo-6e8Usv-j2DKz9ana8rR2JslxjMgR0jxOX9Iz2Y8BF9wQvxQlTzspJJv9w4g==
│   ├── refs.0~Ho4URfFpOlkF_yunDTGIHD26BYRVMIseiNWjFLYEDyBwSdrJ7PDkdieHERv22_n_2ektzRNJ2jMDGHr3o2QIaQ==
└── Info.plist

以下のコマンドでsample.xcresultをjson形式で見ることができます。

xcrun xcresulttool get --path sample.xcresult --format json 

出力結果の行数がかなり長いため、どこに何が書いてあるのか構造が理解しにくいでしょう。
.xcresultの構造がどうなっているのかを確認するためだけのサブコマンドも用意されています。
それを叩いて全体像を確認しましょう。

xcrun xcresulttool formatDescription get 

とはいえ、これはこれでかなり項目が多くわかりにくいと思います。
それぞれ何を示しているのかは、目的に応じて都度実際に記載されている内容と照らし合わせながら推測していくしかないです。

ここで必要とされている.xccovreport.xccovarchiveを生成するためのIDだけでいえば、以下の項目を見れば良いです。

  - CodeCoverageInfo
    * Kind: object
    * Properties:
      + hasCoverageData: Bool
      + reportRef: Reference?
      + archiveRef: Reference?

このreportRefarchiveRefにそれぞれのIDが記載されています。

それではsample.xcresultの該当する項目を見てみましょう。

...
...
        "actionResult" : { 
          "_type" : { 
            "_name" : "ActionResult"
          },  
          "coverage" : { 
            "_type" : { 
              "_name" : "CodeCoverageInfo"
            },  
            "archiveRef" : { 
              "_type" : { 
                "_name" : "Reference"
              },  
              "id" : { 
                "_type" : { 
                  "_name" : "String"
                },  
                "_value" : "0~_8vRmaabmeE6qxk5SPr5-zItlyCmdeOhHEOXQBtDHePMDaWSwSoV1O_YVbeEHQfau3uwQ1RRdKGPf6vVryQOSA=="
              }   
            }, 
            "reportRef" : { 
              "_type" : { 
                "_name" : "Reference"
              },  
              "id" : { 
                "_type" : { 
                  "_name" : "String"
                },  
                "_value" : "0~U9Mb0J7rRAGvBun8pW62sZBA0Heg4CVvkgqWGHO3b7TdzHFcNva6h39z3F0poVgIlJYNFxqvO5ZakFc7MDpNJw=="
              }   
            }   
          }, 
...
...

読み方がややこしいのですが、ぞれぞれ"archiveRef" > "id" > "_value""reportRef" > "id" > "_value" を見れば大丈夫です。

ここでは

xcarchiveのID: 
  "0~_8vRmaabmeE6qxk5SPr5-zItlyCmdeOhHEOXQBtDHePMDaWSwSoV1O_YVbeEHQfau3uwQ1RRdKGPf6vVryQOSA=="
xcreportのID:
    "0~U9Mb0J7rRAGvBun8pW62sZBA0Heg4CVvkgqWGHO3b7TdzHFcNva6h39z3F0poVgIlJYNFxqvO5ZakFc7MDpNJw=="

です

.xccovarchiveのエクスポート

xcresulttoolのexportコマンドでエクスポートします。pathにxcresultを指定して、idにxcresultから見つけてきたxcarchiveのidを指定します。xccovarchiveはディレクトリのため、typeにはdirectoryを指定する必要があります。

xcrun xcresulttool export --path sample.xcresult --id 0~_8vRmaabmeE6qxk5SPr5-zItlyCmdeOhHEOXQBtDHePMDaWSwSoV1O_YVbeEHQfau3uwQ1RRdKGPf6vVryQOSA== --type directory --output-path export_0.xccovarchive

カレントディレクトリにexport_0.xccovarchiveが生成されます。

.xccovreportのエクスポート

xcresulttoolのexportコマンドでエクスポートします。pathにxcresultを指定して、idにxcresultから見つけてきたreportRefのidを指定します。xccovreportはディレクトリのため、typeにはfileを指定する必要があります。

xcrun xcresulttool export --path sample.xcresult --id 0~U9Mb0J7rRAGvBun8pW62sZBA0Heg4CVvkgqWGHO3b7TdzHFcNva6h39z3F0poVgIlJYNFxqvO5ZakFc7MDpNJw== --type file --output-path export_0.xccovreport

カレントディレクトリにexport_0.xccovreportが生成されます。

xccovarchiveとxccovreportからカバレッジ情報を取得する

続いて.xccovarchive.xccovreportでできることを見ていきます。

特定のファイルに対して、行単位のカバレッジを取得する

以下のコマンドでカバレッジが見れるファイル一覧を出力します。

xcrun xccov view --file-list export_0.xccovarchive

そのうちのどれかを使って以下のコマンドを打つと、ファイルの各行のカバレッジがどうなっているかが見れます。
アスタリスクがついている部分はカバレッジ計算の対象外となっている部分で、数値はそこを通った回数です。
Xcode上でCode Coverageを表示させた時と同じ形になっています。

 1: *
 2: *
 3: *
 4: *
 5: *
 6: *
 7: *
 8: *
 9: *
10: *
11: *
12: *
13: *
14: *
15: *
16: *
17: *
18: *
19: *
20: *
21: *
22: *
23: *
24: *
25: *
26: *
27: *
28: *
29: *
30: *
31: *
32: *
33: *
34: *
35: *
36: *
37: *
38: 49
39: 49
40: 49
41: *
42: *
43: *
44: *
45: 49
46: 49
47: 49
48: *
49: *
50: 0
51: 0
52: 0
53: *
54: *
55: *
56: *
57: 0
58: 0
59: 0
60: 0
61: 0
62: 0
63: 0
64: 0
65: *
66: *
67: *
68: *
69: *
70: *
71: *
72: *
73: 49
74: 49
75: 49
76: 49
77: 49
78: 49
79: 49
80: *
81: *
82: *
83: *
84: *
85: *
86: *
87: 0
88: 0
89: 0
90: 0
91: 0
92: 0
93: 0
94: 0
95: 0

特定ターゲットのファイル毎のカバレッジを取得する

以下のコマンドでファイル毎のカバレッジを出力します。注意点としては指定するターゲットはパッケージ名です。

xcrun xccov view --files-for-target Hoge.framework export_0.xccovreport

以下のようにファイル毎のカバレッジを表形式で見ることができます。

ID Name                                               # Functions Coverage         
-- -------------------------------------------------- ----------- ---------------- 
0  /Path/Hoge/Sources/Class1.swift                    8           48.72% (19/39)   
1  /Path/Hoge/Sources/Class2.swift                    2           100.00% (24/24)  

ファイル単位で出力できるということはカバレッジの計算対象外としたいファイルを除くこともできます。
表形式だと扱いにくいため、--jsonをつけてjson形式にするとプログラムから処理しやすくなるでしょう。

xcrun xccov view --files-for-target Hoge.framework export_0.xccovreport --json

ターゲット全体のカバレッジを見る

ターゲット全体のカバレッジを見る場合は以下のコマンドを実行します。

xcrun xccov view --only-targets export_0.xccovreport

ファイル毎のカバレッジと同じように表形式で見れます。

ID Name                   # Source Files Coverage          
-- ---------------------- -------------- ----------------- 
0  Hoge.framework         10             21.07% (326/1547) 
1  HogeTests.xctest       4              32.18% (671/2085) 

他にも、関数単位でのカバレッジを出力するなど色々できます。詳細は初めに書いた通り、helpサブコマンドで確認してください。

複数のカバレッジをマージして総カバレッジを確認する

複数のテストバンドルのカバレッジをまとめることもできます。
テスト対象が同じでテストバンドルが複数に分かれているケースなどで利用できます。

前述までで生成した複数の.xccovreport, .xccovarchiveに対して、mergeコマンドを使うことで実現できます。

以下のようにmergeコマンドの引数に生成したやつを全部指定してやれば大丈夫です。

xcrun xccov merge export_0.xccovreport export_0.xccovarchive export_1.xccovreport export_1.xccovarchive 

こうすると、merged.xcarchive, merged.xcreportがカレントディレクトリに生成されます。

「xccovarchiveとxccovreportからカバレッジ情報を取得する」で書いている内容と同じことをすれば、複数のテストバンドルの総カバレッジを取得できます。

複数のカバレッジデータを使っできることには、他にもdiffを取るなどもがあります。こちらもhelp サブコマンドで詳細が確認できます。

コマンドラインでの出力結果を応用する

このようにカバレッジデータはコマンドライン上で細かく操作することが可能です。
例えば、
「複数のテストバンドルをマージして、不要なファイルを取り除いた上で通った行数/全行数として、行カバレッジの計算を行う」
などといったスクリプトも実装できます。
それ以外にも様々な応用が考えられます。

まとめ

  • XCTestのテストカバレッジをコマンドライン上で出力できる
  • 複数カバレッジのマージやdiffを取るなどできる
  • スクリプトと組み合わせて様々な応用が可能

参考資料

Xcodeのテストとしてはこちら

公式の資料としてはWWDC2019のものを見るのが良いです

網羅的なドキュメントはあまり存在していませんが、日本語の資料としては以下が一番詳細に記載されていると思います。

あとは各コマンドのヘルプと以下のようなライブラリの実装から読み解いていくのがいいです。

4
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?