初めに
まず最初に Xcode の Playground とは違います のでmm
今回は Swift Playgrounds でアプリを開発する場合のお話です。
Swift Playgrounds とは
2016年に登場した iPad 上で Swift を学習できるアプリです。
当初はテンプレートの教材を Swift で学習するだけでした。
現在はバージョンが4.1
までリリースされており、iPad のみならず Mac 上でも動作し、実際にリリースできるアプリが作成できるまでになりました。
どんなことができるの?
iOSDC Japan 2022 で LT をしました!
資料をあげたので、見ていただけると少しは理解が深まるかと思いますmm
テストコードの実装方法
今回は一部の作業を iPad ではなく Mac で行う必要があります。
iPad でもファイル操作可能なアプリを入れればできるかと思いますが、未確認です。
1. 前提
Swift Playgrounds
では、テストを書く機構がありません。
また、Xcode でアプリを開いたとしても、追加することはできません。
そこでライブラリを使っていきます。
Swift Playgrounds
で作成するアプリで、テストをかけるようにするライブラリです。
ただし、Swift Playgrounds
は、そもそも複数のターゲットを持てません。
つまり、ライブラリを追加しても、プロダクションコードの中にテストを書くことになります。
ライブラリのコードは、ビルドして配布する過程で除かれるので、容量にはほとんど影響がありません。
補足
ライブラリのテスト実行方法ですが、以下の記事にも言及されている方法なのではないかと思います
(ざっとライブラリコードを見た感じですが)
内部的には Objective-C
のランタイムを使って、テストの実行を行なっていそうです。
2.導入
① Swift Package Manager での導入
Swift Playgrounds
のデフォルトで唯一サポートされている Swift Package Manager
で追加します。
② コンパイル時の設定を追加
① だけでも動作しますが、フラグを追加しないとアプリ配布時にテストコードが含まれてしまいます。それを防ぐための設定をします。
アプリの「パッケージの内容を表示」で、中身を見ることができます。
そこにある、 Packaget.swift
に設定を追加します。
/* 略 */
targets: [
.executableTarget(
name: "AppModule",
dependencies: [
.product(name: "PlaygroundTester", package: "playgroundtester")
],
path: ".",
swiftSettings: [
.define("TESTING_ENABLED", .when(configuration: .debug)) // これ
]
)
]
/* 略 */
これで導入は完了です。
3. 実装
Xcode のテストとは違い、プロダクションコードで実行する必要があります。
その結果を View で表示して確認できるようにしたのが、このライブラリの特徴です。
struct Myapp: App {
init() {
PlaygroundTester.PlaygroundTesterConfiguration.isTesting = true
}
var body: some Scene {
WindowGroup {
PlaygroundTester.PlaygroundTesterWrapperView {
// YourContentView()
}
}
}
}
フラグを false
にすれば自身が作成した View
を閲覧できます。仮に true
のまま間違ってストアに上げても、内部的にはアプリの View
を返すようになっているのでそこは安心です。
実際の例を交えて見ていきます。
ここでは、以下のモデルをテストする場合を想定していきます。
class Model {
var counter = 0
func add() {
counter += 1
}
func remove() {
counter -= 1
}
}
テストコードはこのようになります。
import PlaygroundTester // <- テストできるようにする
@objcMembers // <- これがないとテストが動かない
final class ModelTest: TestCase {
func testAdd() {
let model = Model()
AssertEqual(model.counter, other: 0)
model.add()
AssertEqual(model.counter, other: 1)
}
func testRemove() {
let model = Model()
AssertEqual(model.counter, other: 0)
model.remove()
AssertEqual(model.counter, other: -1)
}
}
-
PlaygroundTester
のimport
をする - テストクラスに
@objcMembers
をつける
を忘れないようにしましょう。
また、その際の View
ではこのように結果を確認できます。
Xcode のテストと同様に testXXX
となっている関数のみがテストとして実行されます。
また、Assert 系も一通りあるので、Xcode のテストと同じような書き方を実現できます。
public func Assert(_ value: Bool, message: String = "")
public func AssertFalse(_ value: Bool, message: String = "")
public func AssertEqual<T: Equatable>(_ value: T, other: T, message: String = "")
public func AssertNotEqual<T: Equatable>(_ value: T, other: T, message: String = "")
public func AssertNil<T>(_ value: T?, message: String = "")
public func AssertNotNil<T>(_ value: T?, message: String = "")
public func AssertUnwrap<T>(_ value: T?, message: String = "") throws -> T
public func AssertExpectations(_ expectations: [Expectation], timeout: TimeInterval)
全てあるわけではないので、アップデートを待ちましょう。
(またはPRを投げましょう!)
ちなみにライブラリの実装的に
AssertEqual(期待値, other: 受け取った値)
のようですが、違和感があるので自分は逆に実装しています。
4. 出来ないこと
個人で開発されているライブラリのため、機能がまだまだ浅い面もあります。
リポジトリにも書かれていますが
- Combine のサポート
- async/await のサポート
このあたりは後回しのようです。
少し冗長ではありますが、Expectation
はあるので、Comebine はしばらくはそれで書くしかなさそうです。
ロードマップがあり、Github の issue としては積まれているので、気長に待つかPRを投げましょう。
終わりに
今回は自前でテストコードを書く方法を紹介しました。
Swift Playgrounds
はアップデートされるたびに出来ることがかなり増えています。
なので、近い将来サポートされることを願いつつ...といったところでしょう。
リポジトリ
以下に導入したアプリがあるので、参考程度に見ていただけますと🙏
その他
関連記事はこちら