SwiftUIも結構理解がすすんだので、SwiftUIとTDDを共存する方法をきちんと確認しておこうかと。
テスト対象とするアプリ
今回のアプリは、テキストフィールドがあって、そこに名前を入力してもらって、その人の肩書き(?)を
表示するアプリを対象としましょう。例えば、”Keisuke Honda”と入力すると”Soccer Player”と表示される感じです。
なお、以下の環境で行っています。
- XCode11.4
- Swift5
- iOS向けアプリ
さっそくプロジェクトを作りましょう
TDDの導入
プロジェクトを作る時に、UnitTest、UITestをプロジェクトに追加するかを選択できます。
今回は、”TDDWithTestField”という名前のプロジェクトを作りました。
どのようなファイルがTDD向けに追加されているか確認しましょう。
TDD設定で追加されるファイル
合わせて、TARGETとして、”TDDWithTestFieldTests”と”TDDWithTextFieldUITests”が追加されています。それぞれ、UnitTest、UIUnitTest用のターゲットです。
UnitTestでアルゴリズムのテスト
テストコードの追加
アプリ実装の前にテストコードを実装するのがTDD流なので、エラーが出ることを承知しつつ、テストコードを実装します。
アルゴリズムのテストということで、入力された名前から、その肩書きが返される関数をテストすることとしましょう。
テストする関数は、クラス”FamousPerson”の”title”というStatic関数で名前(:String)を受け取り、肩書き(:String)で返すような以下の関数をテストすることにしましょう。
それに対応するテスト側のコードは、”FamousPersonTest”というUnit Testのファイルを新しく追加して、その中に以下のような関数を追加しましょう。(既存のUnitTest用ファイルに追加してもOKですが、対象クラスごとに分ける方が管理しやすいです)
1 2 3 |
func testTitle() throws { XCTAssertEqual(FamousPerson.title(name: "Keisuke Honda"), "Soccer Player") } |
アルゴリズムコードの実装
まずは、コンパイルエラーを解消するために、クラス定義とメソッド定義を行いました。
1 2 3 4 5 |
class FamousPerson { static func title( name: String) -> String { return "unknown" } } |
上記のコードでコンパイルエラーは無くなります。それでは、さっそく、テストしましょう。
現在の実装は、”unknown”と返すだけのコードなので、コンパイルできても、テストを実行するとFailedとレポートされます。
アルゴリズムコードの改良
名前→タイトルの対応付けをMAPに保存しておくことにしましょう。
1 2 3 4 5 |
static func title( name: String) -> String { let nameTable:[String:String] = ["Keisuke Honda":"Soccer Player"] return nameTable[name, default: "unknown"] } |
テストも成功するようになりました。
アルゴリズムを改良してくのであれば、”別のテストケースを追加 -> アルゴリズムのコードを改良”を繰り返していくことになります。
ここでは、アルゴリズムはOKとして、UIのテストに進みます。
UIのテスト
以下をテストすることとしました。
- 名前の入力フィールドが見えていること
- place holderのテキストが期待値であること
- “Keisuke Honda”を入力すると、ラベルが、”Soccer Player”になること
- ”HenoHeno Moheji”を入力すると、ラベルが、”unknown”になること
UIテストの実装
ということで、以下を実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func testKeisukeHonda() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() // check textfield availability let textField = app.textFields["NameTextField"] XCTAssertTrue(textField.isEnabled) XCTAssertEqual(textField.placeholderValue, "enter name") // check behavior textField.tap() textField.typeText("Keisuke Honda") let titleLabel = app.staticTexts["TitleLabel"] XCTAssertEqual(titleLabel.label, "You are Soccer Player") } |
(UI実装していないので、当たり前ですが)、失敗します。
UIの実装
では、UIの実装をしていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@State var userName:String = "" var body: some View { VStack { Spacer() TextField("enter name", text: $userName) .textFieldStyle(RoundedBorderTextFieldStyle()) .accessibility(identifier: "NameTextField") Spacer() Text("You are \(FamousPerson.title(name: userName) )") .accessibility(identifier: "TitleLabel") Spacer() } } |
UITestの確認
改めて、UIテストを実行するとOKになることが確認できます。
まとめ
SwiftUIでもUIKitでもおおよそ同じようにTDDすることができます。
それさえわかれば、あとは、UIKit,SwiftUI同じです。
UIの改良、さらに超高効率で改良するためのTips
テキストフィールドが左右一杯に広がっているのって、違和感ありますよね。
TextFieldのmodifierで、左右に若干のスペースを設定できるものがあったと思うのですが・・・と思って、探し回りました。
“.padding()”でした!
改良したUIは、こちら。
SwiftUIには、このように”大抵の人が欲しいと思う調整”がすでに揃っている気がします。
自分で探すのも1つですが、”SwiftUI Views Mastery Bundle”という本が超便利です。
Sponsor Link