iOSアプリをCIでビルドしたりテストするのに fastlane を使っています。fastlane には snapshot というアクションが用意されており、UIテスト中にスクリーンショットを撮影して結果をHTMLで見ることができます。
便利なのですが、Xcode 9 から XCUITest自体にスクリーンショットを取得する機能が導入されました。
snapshot の代わりにこちら使っていこうと思ったのですが、snapshot を使わないと fastlane でテストを実行したときにスクリーンショットを含んだ HTML ファイルを生成することが現状できないんですよね……。そこで、作ってみました。使い方は README.md を読んでください。
- https://github.com/taisukeh/xcode-test-reporter
- https://github.com/taisukeh/fastlane-plugin-xcode_test_reporter
こんな感じのHTMLができます。
以下は実装のメモになります!
XCUITest でのスクリーンショットの撮影
XCTAttachment を使います。XCUItest で以下のコードを実行します。
let attachment = XCTAttachment(screenshot: XCUIScreen.main.screenshot())
attachment.lifetime = .keepAlways
attachment.name = "MyScreenshot"
add(attachment)
lifetyme
に .keepAlways
を指定するとテスト成功時にもスクリーンショットが残るようになります。デフォルトではテスト失敗時にのみ残ります。name
にはどの画面かなどの名前を指定します。
テスト中に毎回このコードを書くのは面倒なので、extension
にメソッドを定義しておくと良いと思います。
extension XCTestCase {
func capture(_ name: String, lifeTime: XCTAttachment.Lifetime = .deleteOnSuccess) {
let attachment = XCTAttachment(screenshot: XCUIScreen.main.screenshot())
attachment.lifetime = lifeTime
attachment.name = name
add(attachment)
}
}
テストを実行すると、テスト結果画面のクリップアイコンからスクリーンショットを見れるようになります。
テスト結果は plist
で以下に保存されています。
~/Library/Developer/Xcode/DerivedData/<your_app>/Logs/Test
またスクリーンショットは
~/Library/Developer/Xcode/DerivedData/<your_app>/Logs/Test/Attachments
に残っています。plist の中を見るとテストの成否や attachments の情報、実行時間などの情報が記録されており十分な情報がありそうです。
plist をパースし HTML を生成するツールの作成
plistをパースする処理は swift なら簡単にかけそう & Codable 使えば楽に書けそうと思ったので swift で作成しました。
CLI
swift での CLI ツールを作成には Swift Package Manager を使います。
swift package init --type executable
を実行するとプロジェクトの雛形ができます。あとは Sources
以下に実装を Tests
以下に書いていきます。
CLI のオプションのパースに使うツールを探してみると、Swift Package Manager に添付されているものを使う方法があるとのことで ArgumentParserが便利すぎる件 [SwiftPM] を使いました。
plistのパース
plist のパースには Codable を使いました。
public struct Report: Codable {
let FormatVersion: String
let RunDestination: RunDestination
let TestableSummaries: [TestableSummary]
}
public struct RunDestination: Codable {
let LocalComputer: LocalComputer
let Name: String
let TargetArchitecture: String
let TargetDevice: TargetDevice
}
public struct TestableSummary: Codable {
let ProjectPath: String
let TargetName: String
let TestName: String
let Tests: [Test]
}
...
このように plist の構造そのままの定義を書いておくと、
func decodePlist<T>(_ type: T.Type, plistUrl: Foundation.URL) throws -> T where T : Decodable {
let data = try Data(contentsOf: plistUrl)
let decoder = PropertyListDecoder()
return try decoder.decode(type, from: data)
}
let report: Report = try decodePlist(Report.self, plistUrl: plistUrl)
だけでパースが完了します。あとはこの情報を元に HTML を生成するだけです。
単体テスト実行
Tests ディレクトリ以下にテストを書くと
swift test
だけでテストが実行できるのは良いのですが……、よくあるテストツールのようにテスト結果を色付きでわかりやすく表示してくれたりはしません。xcprettyを使うといい感じになりました。
swift test 2>&1 | xcpretty
また今回 CircleCI の siwft docker image でテストを実行するようにしました。Mac上でテストを実行した際は、テストケースの test
という名前がついたメソッドを自動で見つけて実行くれるのですが、Linux で実行する際には自分でテストメソッドを一覧にしなければなりませんでした。
LinuxMain.swift
というファイルを用意して、
import XCTest
@testable import LibTests
XCTMain([
testCase(PlistParserTests.allTests),
])
各テストクラスで allTests
を定義しいちいちメソッドを配列にして渡す必要があります。
class PlistParserTests: XCTestCase {
override func setUp() {
super.setUp()
}
static var allTests = [
("testParse", testParse),
]
...
これはテストケースが多くなってくると面倒そうですね……。
fastlane plugin
fastlane には作者直々の trainer という、Xcodeのテスト結果を JUnit の XML に変換してくれる plugin があります。plist をパースするすればよいということはこの trainer から知りました。今回作成したツールと trainer はやっている処理自体は同じです。HTML を出力するかどうかという違いがあるだけです。
作成した plugin のオプションの指定などは trainer を真似しました。また、JUnit の XML の出力処理も実装しているため、trainer を置き換えることもできます。
まとめ
snapshot を使わなくても XCUITest でスクリーンショットを撮影 & HTML 出力できるようにしました
Swift Package Manager を使ったツールと、fastlane pluginを作成しました。trainer を置き換える感じで使用できます。
- https://github.com/taisukeh/fastlane-plugin-xcode_test_reporter
- https://github.com/taisukeh/fastlane-plugin-xcode_test_reporter
もし良ければいいねや GitHub のスターをください!
以上、