おいしい健康 開発者ブログ

株式会社おいしい健康で働くエンジニア・デザイナーが社内の様子をお伝えします。

XcodeGenを使ったiOSプロジェクトに、XCUITestとXCTestを導入した話

はじめまして。昨年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ボタンが有効になります。
    f:id:oishi-kenko:20190719144601p:plain
    Record UI Test Button
  • そのボタンを選択すると、アプリが起動します。
  • アプリ内のボタン等をタッブすると、挙動が自動でファイルに記述されていきます。スゴイ!
  • テストを実行するには、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 でテストの重要性を再認識したからでした。 まだまだスタートラインに立っただけの状態ですが、少しずつ自動テストの環境を育てていこうと思います!