はじめまして。昨年10月からiOSエンジニアとして入社しました、佐々木と申します。 子育て中のため時短で働いております。
今回はおいしい健康アプリのプロジェクトに、Apple公式のテストフレームワークであるXCUITest(UIテスト)とXCTest(Unitテスト)を導入した時の事を書きます。 XcodeGenというツールにも対応したので、そこも含めての内容となります。
XCUITestを導入
まずは、XCUITestの導入手順になります。
- File> New> Target...> iOS UI Testing Bundle> Next
- ProductNameに
oikenUITests
と入力> Finish - プロジェクト配下に、ProductNameと同じディレクトリとファイルができます。
- ファイルを開き、testExampleメソッド内にカーソルを置くと、赤丸の
Record UI Test
ボタンが有効になります。 - そのボタンを選択すると、アプリが起動します。
- アプリ内のボタン等をタッブすると、挙動が自動でファイルに記述されていきます。スゴイ!
- テストを実行するには、
cmd+U
キーです。
XCUITestの方はこれでOKです。
XCTestを導入
次に、XCTestの導入手順になります。
- File> New> Target...> iOS Unit Testing Bundle> Next
- ProductNameに
oikenTests
と入力> Finish - こちらもプロジェクト配下に、ProductNameと同じディレクトリとファイルができます。
- ファイルを開き、試しにテスト処理を入れます。 その際アプリの処理を呼ぶには、importの並びに以下のような記述が必要となります。
@testable import oiken
しかしこのままだと、No such module ...
でエラーになってしまいます。
プロジェクトの設定ファイル等を修正
エラーを解消するには、いくつか設定ファイルを変更する必要があります。 修正箇所はstack overflowの投稿をいくつか参照してを見つけました。
- Podfileにテストのターゲットを追加します。
修正前
pod 'XXX' pod 'XXX' target 'oiken' do end use_frameworks!
修正後
def standard_pods pod 'XXX' pod 'XXX' end target 'oiken' do standard_pods end target 'oikenTests' do inherit! :search_paths standard_pods end target 'oikenUITests' do inherit! :search_paths standard_pods end
修正後は以下のコマンドで、CocoaPodsを更新します。
bundle exec pod install
oiken> Product Module Name> アプリ名を変更します。 開発スキーマの末尾に追加していた
Dev
の文字列をとり、どのスキーマもoiken
にしました。oikenTestsとoikenUITests> Build Setting> Defines Module> Yesに変更します。
oikenTestsとoikenUITests> iOS Deployment Target> iOS10.3に変更します。 テストターゲットの
Deployment Target
がデフォルトの最新になっていたので、変更しました。
これでビルドが通るようになります。
cmd+U
キーで、テストを実行できました!
プロジェクトの環境にもよりますが、このような手順でテスト環境が整います。 しかし今回は、XcodeGenの対応も必要となります。
XcodeGen
おいしい健康アプリのプロジェクトは、複数のエンジニアが開発しているため、以前からXcodgeGenを導入しています。 https://github.com/yonaskolb/XcodeGen
XcodeGenは、project.pbxprojのコンフリクトを防ぐためのもので、ymlに設定情報を書き、そのymlからproject.pbxprojを生成してくれるツールです。 おかげ様で最近はコンフリクトする事が少なくなり、とても生産性が上がった気がします!
しかしテストを導入する場合には、project.ymlにその変更を反映しなくてはなりません。
project.ymlに変更を反映
XcodeGenはOSSなので、GitHubにあるドキュメントやソースコードを見ながら修正箇所を見つけました。 セキュリティ上全部は載せられないのですが、部分的に紹介します。
- プロジェクトファイルを戻します。 まずは以下のコマンドで、project.pbxprojを元に戻します。 (厳密には、project.ymlから生成するため、変更が元に戻ってしまうのです)
xcodegen & bundle exec pod install
この状態だと、cmd+U
キーを押しても何も起こりません。
Product Module Name
追加します。
settingGroups: oiken: base: PRODUCT_MODULE_NAME: "$(TARGET_NAME)"
- UIテストを追加します。
oikenUITests: type: bundle.ui-testing platform: iOS sources: - oikenUITests settings: groups: - oiken base: GCC_PREPROCESSOR_DEFINITIONS: "$(inherited) COCOAPODS=1" INFOPLIST_FILE: "$(SRCROOT)/oikenUITests/Info.plist" IPHONEOS_DEPLOYMENT_TARGET: "10.3" CODE_SIGN_STYLE: Automatic DEFINES_MODULE: true PRODUCT_BUNDLE_IDENTIFIER: "oikenUITestsのバンドルID" configs: Develop: TEST_HOST: "$(BUILT_PRODUCTS_DIR)/oikenDev.app/oikenDev" Release: TEST_HOST: "$(BUILT_PRODUCTS_DIR)/oiken.app/oiken" dependencies: - target: oiken
- Unitテストを追加します。
oikenTests: type: bundle.unit-test platform: iOS sources: - oikenTests settings: groups: - oiken base: GCC_PREPROCESSOR_DEFINITIONS: "$(inherited) COCOAPODS=1" INFOPLIST_FILE: "$(SRCROOT)/oikenTests/Info.plist" IPHONEOS_DEPLOYMENT_TARGET: "10.3" CODE_SIGN_STYLE: Automatic DEFINES_MODULE: true PRODUCT_BUNDLE_IDENTIFIER: "oikenTestsのバンドルID" TEST_HOST: "$(BUILT_PRODUCTS_DIR)/oiken.app/oiken" dependencies: - target: oiken
- 各スキーマのTestにテストターゲットを追加します。
schemes: Develop: test: config: Develop gatherCoverageData: true targets: - name: oikenUITests - name: oikenTests Release: test: config: Release gatherCoverageData: true targets: - name: oikenUITests - name: oikenTests
- ymlからproject.pbxprojを生成します。 修正後は以下のコマンドで、再生成します。
xcodegen & bundle exec pod install
ymlに記述した設定は、階層がずれるだけでプロジェクトファイルに反映されず、、結構苦労しました。。。
cmd+U
キーで、テストが実行できます!
これでテスト環境が構築できました。
おわりに
今回テストを導入したきっかけは、try! Swift Tokyo 2019 でテストの重要性を再認識したからでした。 まだまだスタートラインに立っただけの状態ですが、少しずつ自動テストの環境を育てていこうと思います!