はじめに
iOS #2 Advent Calendar 2020 9日目の記事です。
こんにちは。PORT新卒エンジニアのしゅんいち(shxun6934)です。
個人でSwiftUIをこっそり勉強して居たのですが、ふと思いました。
「SwiftUIのUIテストって、どー書いたらいいの???」
気になったので、いろいろ調べたらいいブログ記事を見つけました。
この記事で学んだことを自分なりに要約して、共有したいと思います!
SwiftUIのテストの書き方
SwiftUIのテストは、今までのUITestのようには書けないようです。
SwiftUIは、Viewに対してアクセスするプロバイダを提供していないので、手動でViewにアクセスし、テストを検証するのが難しいです。
WWDC2019でAppleは、SwiftUIのテストに対しては、テストしたいViewのプレビューをそれぞれ作成し、それを検証する必要があると言っていました。
しかし、このテスト方法は、実際のViewをテストしているわけではないので、信頼性に少しかけるところがあります。
また、拡張性があまりないのも懸念されています。他言語対応やテーマ(ライトとダーク)に対して、それぞれのテストがあまりできないのもあるそうです。
これらの改善点は、SnapShotTestを使用することで改善されます!
SnapshotTestとは、、、
SnapShotTestは、すでに構成されているViewからスナップショットを生成し、検証するViewが正しい状態であるかどうかをスナップショットとの差分から判別するテスト方法です。
詳しくは、ZOZOテクさんのブログに書いてあります!
SnapShotTestとは
従来のUIテストとの違い
今までのUIユニットテストは、いくつかのコードを実行して、入力が正常に出力を生成するかどうかを検証します。
一方でスナップショットテストでは、テストの出力をベースラインスナップショットと呼ばれる、以前のテスト実行された出力と比較して、正常になっているかを検証します。
SnapShotTestを使用したSwiftUIのテストは、
- SwiftUIで構成されたViewをさまざまな状態でレンダリングしておく
- 一方で、正常なViewのスナップショットを取得する
- 以前に作成したベースラインスナップショットと比較する
- 差がなかったら成功、あったら失敗する
のフローになります。
失敗した場合は、以下の2つのことが考えられますね。
- 変更が意図されており、ベースラインスナップショットを更新する必要がある。
- 予期しない変更であり、コードを修正する必要がある。
このように、UIを別のUIと比較して検証するのが、SnapShotTestの特徴です。
なので、UIを検証するユニットテストにかなり向いています!
実際に書いてみよう
なんとなく、SnapShotTestのことがわかったので、簡単なものを実際に書いていきましょう!
swift-snapshot-testing
今回は、SwiftでSnapShotTestができるswift-snapshot-testingを使用していきます。
せっかくのなので、Xcode11から追加された、Swift Package Managerを使用してみましょう!
導入
File > Swift Packages > Add Package Dependency
を選択します。
スクリーンショット 2020-12-09 21.03.23.png
そしたら、追加するパッケージの入力する画面が出るので、
https://github.com/pointfreeco/swift-snapshot-testing.git
を入力し、Versionを指定します。(最新版で大丈夫そうです。)
スクリーンショット 2020-12-09 21.20.16.png
gitによってライブラリがXcodeに読み込まれます。
最後のダイアログには注意が必要です。
ライブラリを使用するTargetを指定するのですが、公式のREADMEにも書いてあるように、
デフォルトだと、プロジェクトのメインアプリケーション/フレームワークターゲットに設定してしまうので
テストターゲットにしてあげましょう。
コード
記事と同じように、SendButton
を作成します。
struct SendButton: View {
let onAction: () -> Void = {}
var body: some View {
Button(
action: onAction,
label: {
HStack {
Image(systemName: "square.and.arrow.up")
Text("送る")
}
})
}
}
# プレビュー
struct SendButton_Preview: PreviewProvider {
static var previews: some View {
SendButton()
.previewLayout(PreviewLayout.sizeThatFits)
.padding()
}
}
テストコードは以下。
import XCTest
import SnapshotTesting
import SwiftUI
@testable import SnapshotTestingSwiftUI
private let referenceSize = CGSize(width: 150, height: 50)
class SendButtonTests: XCTestCase {
func testDefaultAppearance() {
assertSnapshot(
matching: SendButton().referenceFrame(),
as: .image(size: referenceSize)
)
}
}
private extension SwiftUI.View {
func referenceFrame() -> some View {
self.frame(width: referenceSize.width, height: referenceSize.height)
}
}
ここで、プロジェクトに成功する画像をあらかじめ入れておいてください。
そうするとテストが通ると思います!
まとめ
結構簡単にかけるなーという印象です。
SnapshotTestという概念が個人的に新しい概念だと思うので、書いていて楽しかったですし、検証していることが正常なViewの検証なので、わかりやすいなと思いました。
これをもう少し使いこなせるようになりたいですね!!
(今回使用しているライブラリがなかなか読み込まれなくて苦戦しました。。。)