Xcode
iOS
XCTest
Swift
UITest

XCTestのUIテストの結果をイイ感じで見たい

直近で新たに画面を作成する案件があったんですが、もういい加減テスト端末を用意したりたくさんのシミュレーターを起動したりするの面倒ですよね。
僕はこれまで Lunch というライブラリを作成してUIテストから直接画面を指定して起動出来る様にしたり、
最近アップデートした個人アプリのWebCollector ではUIテストの結果をスクリーンショットに保存する様な工夫をしてきました。
ただ、そこで困っていたのが撮影したスクリーンショットを イイ感じに表示したい という事でした。

そういう時にパッと思い浮かぶのはFastlane等のツール群ですが、そもそも snapshot を利用していないので厳しいのかなと思いました。

-resultBundlePath オプション

xcodebuild のオプションに -resultBundlePath を設定する事で下記の様なファイル達が作成されます

Screen Shot 2018-04-24 at 20.43.39.png

この中にある TestSummaries.plist というものを見てみると下記の様になっています。

Screen Shot 2018-04-24 at 21.02.48.png

また、Attachments フォルダの中にはスクリーンショットが保存されています。

Screen Shot 2018-04-24 at 21.03.55.png

TestSummaries.plist を奥底まで辿っていくと ActivitySummaries がありスクリーンショットを撮影したテストには Attachments という配列がいます。
Attachment オブジェクトには Filename があり、これが先ほどの Attachments フォルダの中にあるファイル名と紐づいています。

CodableTestSummaries.plist をパース

JSONのエンコード/デコードに注目されがちなSwift 4から実装されている Codable ですが、PropertyListEncoderPropertyListDecoder があるのをお忘れではないでしょうか?
下記の様にすればplistファイルを簡単にデコードする事ができます。

let testSummariesURL = URL(fileURLWithPath: bundlePath).appendingPathComponent("TestSummaries.plist")
let data = try Data(contentsOf: testSummariesURL)
let plistDecoder = PropertyListDecoder()
let testSummaries = try plistDecoder.decode(TestSummaries.self, from: data)

デコードするモデルは TestSummaries.plist とにらめっこしながら頑張って書き起こします。

struct TestSummaries: Codable, Equatable {

    let formatVersion: String

    let testableSummaries: [TestableSummary]

    private enum CodingKeys: String, CodingKey {
        case formatVersion = "FormatVersion"
        case testableSummaries = "TestableSummaries"
    }
}

struct TestableSummary: Codable, Equatable {
    let diagnosticsDirectory: String
    let projectPath: String
    let targetName: String
    let testName: String
    let testObjectCLass: String
    let tests: [Test]

    private enum CodingKeys: String, CodingKey {
        case diagnosticsDirectory = "DiagnosticsDirectory"
        case projectPath = "ProjectPath"
        case targetName = "TargetName"
        case testName = "TestName"
        case testObjectCLass = "TestObjectClass"
        case tests = "Tests"
    }

}

struct Test: Codable, Equatable {
    let duration: TimeInterval
    let subTests: [Test]?
    let activitySummaries: [ActicitySummary]?
    let testIdentifier: String
    let testName: String
    let testObjectClass: String
    let testStatus: String?
    let testSummaryGUID: String?

    private enum CodingKeys: String, CodingKey {
        case duration = "Duration"
        case subTests = "Subtests"
        case activitySummaries = "ActivitySummaries"
        case testIdentifier = "TestIdentifier"
        case testName = "TestName"
        case testObjectClass = "TestObjectClass"
        case testStatus = "TestStatus"
        case testSummaryGUID = "TestSummaryGUID"
    }
}

.
.
.

パースが完了したらあとは欲しい情報をまとめて表示するだけですね!

Terminalで動作するコマンドラインツールを作ってみた

と、自分で作るのはなかなか大変ですよね。
今回初めてMacのTerminal上で動作するコマンドラインツールを作ってみました。

Githubはこちらです
fromkk/TestSummaries

インストール方法

Homebrewを利用してインストールする場合は下記のコマンドでインストール可能です。

brew install fromkk/TestSummaries/testsummaries

(Homebrew経由でインストールさせる方法はSwift Package Manager(SwiftPM)で作ったコマンドラインツールをHomebrewに登録する方法こちらを参考にさせて頂きました :bow: )

また、Githubからクローンする場合は下記の通りです。

git clone git@github.com:fromkk/TestSummaries.git
cd ./TestSummaries
make install

利用方法

利用する場合は test-summaries コマンドを利用します

test-summaries [--resultDirectory <resultDirectory>] | [--bundlePath <bundlePath>] --outputPath <outputPath>

オプションは下記の通りです。

オプション名 説明
--resultDirectory 複数のテストのスクリーンショットをまとめて表示したい場合に、親ディレクトリのパスを指定します
--bundlePath 1つのテストのスクリーンショットを表示したい場合に、バンドルのパスを指定します
--outputPath 生成したHTMLファイルを保存するパスを指定します

結果

下記の様なHTMLファイルが生成されます :tada:

capture.png

まとめ

念願のスクショをイイ感じに見る事が出来る様になりました。
これまでは1枚ずつ見比べる必要があったのが、HTMLを開くだけで各端末や条件のテストの差異が見れる様になったのは大きな前進な気がします。
ただ、コードはかなり急いで作ったのでかなり粗があるかと思います。
不具合や要望などあればコメントやissue、PRを頂ければと思います :bow:

いいなと思ったら いいね👍🏻Github にスター🌟ください!

(テストの為のツールにテストが書かれていないとは何事か...その内書きます😅)