概要
この記事では、XCTestのカバレッジデータをコマンドライン上で操作する方法に関して解説します。
カバレッジデータを操作するには専用のデータ(.xcresult)とコマンド操作(xccov, xcresulttools)が必要となります。
また、具体的な応用事例として 「複数に分かれたテストバンドルのカバレッジデータをマージして総カバレッジを出す」 というのを行います。
カバレッジの解析に必要な道具を揃える・理解する
まず初めに、カバレッジをコマンドラインで操作するために必要な道具について解説します。
.xcresultファイルを取得する
カバレッジデータを生成するにはXcodeでテストカバレッジ計測をオンにする必要があります。
まず、Scheme一覧のEdit SchemeからTestのタブを開いてチェックをつけます。
その後Cmd+Uでテストを実行すると、テストカバレッジが生成されるようになります。
テストカバレッジはProject Navigatorの「Show Report Navigator」から確認できます。
右クリックをして「Show in Finder」をクリックするとファイルがある場所に移ることができます。
テストカバレッジのデータは.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?
このreportRef
とarchiveRef
にそれぞれの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のものを見るのが良いです
網羅的なドキュメントはあまり存在していませんが、日本語の資料としては以下が一番詳細に記載されていると思います。
あとは各コマンドのヘルプと以下のようなライブラリの実装から読み解いていくのがいいです。