一昨年の12月に博多に来て以来、丸一年ぶりに博多に来ています。
目的はSwift Days Fukuokaに参加するためです。
Swift Days Fukuokaとは
全国のSwiftに何らかの関係があるエンジニアが、福岡に一斉に集まって情報共有をする3日間(1/24 ~ 26)のイベントです。
第11回 HAKATA.swift x Swift愛好会~福岡と東京のSwift勉強会コラボ~ - connpassより引用
初日の今日は
testnight.connpass.com
です!
イベントが発表されてすぐ登壇申し込みをしたんですが、特に発表する内容を考えていなかった中頑張って捻り出したのがTest Planの使い道です。
発表資料
Test Planとは
Xcode 11から追加されたテストに関する新たな機能です。
これまでは端末の状態や設定を変更してテストをしようとすると都度スキームの設定を変更する必要がありました。
そういった場合にCIなどで柔軟なテストを実行しようとするとxcodebuildコマンドで頑張る必要がありました。
例) -testLanguage
や-testRegion
オプション
Test Planを利用するとそういった設定を一つのJSONファイル(.xctestplan
)に書いておき実行時にそれらをまとめて実行することができるようになりました。
参考: Testing in Xcode - WWDC 2019 413
Test Planの設定方法
Schemeの編集画面のサイドバーでTestを選択し、左下の +ボタンからTest Planを追加します。
既にTest Plan未対応のテストが既にある場合は Convert to Test Plans
というボタンから変換も可能です。
ターゲットの追加
続いてテストするターゲットを追加します。
これでターゲットの追加が完了です。 Configurationを選択してみます。
全てのテストで共通の設定がある場合はShared Settings
を設定します。
左下の +ーボタンから構成を追加・削除することができます。
構成を追加するとShared Settings
で設定した項目が継承されながら独自の設定が可能になります。
設定項目
テスト実行時の設定を変更することができます。(引数、環境変数、言語や地域など) また、テストの実行順序やコードカバレッジ、クラッシュ時に関する情報の取得も可能です。
テストの実行
⌘(Command)+Uで選択中の全てのテストが実行されます。
複数のConfigurationが存在する場合でテスト実行ボタンをCtrlキーを押しながらクリックすると特定のConfigurationを選んで実行することができます。
 
コマンドラインからはxcodebuild test -testPlan 'TestPlan名'
でTest Planを指定して実行することができます。
更に詳細については弊社の@akatsuki174さんが書いた記事も参考にしてください。 qiita.com
本題
ようやくここからが本題です。
なんとなくTest Planはよさそうですが、実際にどういう時に利用するのが有効なのでしょうか。
ここでは2つのテストケースでTest Planを利用することで不具合が見つけられそうというものを考えてみました。
翻訳が正しく設定されているか
と言ってみたものの現状のTest Planで大きく活躍しそうなのは言語と地域にまつわるものかと思います。
前提としてこのような見た目のUIがあるとします。
(上も下も「登録」になるようなUIは実際には無いと思いますが一旦進めます)
まずは日本語だけでテストをしてみます。
テストコードはこんな感じにしてみました。
無事テストが通っていることがわかります。
ここでTest Planに英語設定を追加してみます。
テストが失敗しました。
UIを作っていたところのコードの翻訳キーを見ると Register
と Registration
になっていましたね。
テストを修正して再実行してみます。
成功しましたね。
偏った環境でだけテストをしてうまくいっていても、新たな環境を追加することで失敗することがあるかもしれないのでこういった場合にTest Planは有効かもしれません。
Localeの未設定を見つける
日付の単位を言語毎に出し分けしたいことがあったとします。
日本語だと数値をそのまま表示して、英語の場合は、1st, 2nd, 3rd...のような表記にしたいということです。
下記のようなコードがあるとします。
引数は数値の日付と言語を受け取って String
の値を返します。
import Foundation struct DayConverter { static func convert(day: Int, for languageCode: String) -> String { if languageCode == "en" { let day = day let number = NSNumber(value: day) let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .ordinal return numberFormatter.string(from: number)! } else { return String(day) } } }
英語の場合に正しい値を返してもらえれば良いのでまずは英語でのテストをしてみます。
問題なさそうですね。
それでは日本語を追加してテストしてみます。
失敗しましたね。 is not equal to ("第XX")
となっているのが分かります。
元々のコードを修正してみます。
import Foundation struct DayConverter { - static func convert(day: Int, for languageCode: String) -> String { + static func convert(day: Int, for locale: Locale) -> String { - if languageCode == "en" { + if locale.languageCode == "en" { let day = day let number = NSNumber(value: day) let numberFormatter = NumberFormatter() + numberFormatter.locale = locale numberFormatter.numberStyle = .ordinal return numberFormatter.string(from: number)! } else { return String(day) } } }
これで無事テストが通りました。
この場合は実は英語、日本語だけではなく Locale
が持っている Region
情報が大事でした。
そしてほとんどの locale
プロパティは特に値を渡さないと現在の設定をそのまま利用するので、設定忘れがあるとこの例のように複数環境でテストをする際に失敗することがあります。
一連の流れを動画にしましたので良ければご覧ください。
Pros/Cons
ちなみに弊社で開発・運用しているアプリケーションのFammでもTest Planを活用しています。
導入する中で1つ不具合を見つけることができたのですがそれもTest Planのお陰でした。
TestPlanでバグ見つけたっぽい。これは良いぞ。
— かっくん@無職予備軍 (@fromkk) 2019年8月28日
TestPlanを導入するデメリットとしてはテストの実行回数が増えるので単にテストにかかる時間が伸びてしまうことですね。
このあたりは普段使いするTest PlanとCI上で実行するTest Planを分けるなどして運用を設計するのがよいでしょうか。
まとめ
Test Planが出るまでは環境を動的に変更したり複数環境でテストをすることは大変だったので、Test Planのお陰でそういったテストが簡単に実行できるようになりました。
特に必要なDIを忘れていることなどが気付きやすくなったので、是非活用して更に品質の高いプロダクトを作っていきましょう。
P.S.
初日は福岡についてすぐとんかつを食べたり、牛タン・焼肉を食べました。
福岡らしいものを何も食べてない!!
明日は
に参加して
で登壇します。楽しみ!
積極採用中!!
子育て家族アプリFammを運営するTimers inc.では、現在エンジニアを積極採用中!
急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください!
採用HP: http://timers-inc.com/engineerings