web-dev-qa-db-ja.com

テストソースにアプリのソースを含める必要があるのはいつですか?

新しいプロジェクトでは、この簡単なテストがあります

#import <XCTest/XCTest.h>
#import "ViewController.h"

@interface ViewControllerTests : XCTestCase
@end

@implementation ViewControllerTests

- (void)testExample
{ 
    // Using a class that is not in the test target.
    ViewController * viewController = [[ViewController alloc] init];
    XCTAssertNotNil(viewController, @"");
}

@end

ViewController.hはnotテストターゲットの一部ですが、これは問題なくテストをコンパイルおよび実行します。

enter image description here

これは、アプリケーションが最初に(依存関係として)ビルドされ、次にテストがビルドされるためだと思います。次に、リンカはViewControllerクラスが何であるかを判断します。

ただし、テストとViewControllerファイルがまったく同じである古いプロジェクトでは、リンカーフェーズでビルドが失敗します。

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_ViewController", referenced from:
  objc-class-ref in ViewControllerTests.o

このリンカエラーは、新しいXCTestユニットテストターゲットが作成された場合でも発生します。

代わりにこれを回避するために、アプリとテストターゲットの両方にソースを含めることができます(上の画像の両方のボックスにチェックマークを付けます)。これにより、シミュレータのシステムログに重複シンボルのビルド警告が表示されます(シミュレータを開き、cmd- /を押してこれを確認します)。

Class ViewController is implemented in both 
[...]/iPhone Simulator/ [...] /MyApp.app/MyApp and 
[...]/Debug-iphonesimulator/LogicTests.octest/LogicTests. 
One of the two will be used. Which one is undefined.

これらの警告により、次の例に示す問題が発生する場合があります。

 [viewController isKindOfClass:[ViewController class]]; // = NO
 // Memory address of the `Class` objects are different.

 NSString * instanceClassString = NSStringFromClass([viewController class]);
 NSString * classString         = NSStringFromClass([ViewController class]);

 [instanceClassString isEqualToString:classString]; // = YES
 // The actual class names are identical

問題は、古いプロジェクトのどの設定で、アプリケーションソースファイルをテストターゲットに含める必要があるかということです。


コメントの要約

稼働中のプロジェクトと稼働していないプロジェクトの間:

  1. リンカ出力(Ldで始まるコマンド)に違いはありません。
  2. ターゲットの依存関係に違いはありません(テストターゲット(アプリ)に1つの依存関係があります)
  3. リンカーの設定に違いはありません。
68
Robert

これを理解するのに少し時間を費やしました。

このドキュメント を読むと、Xcodeにはテストを実行するための2つのモードがあることがわかります。論理テストとアプリケーションテスト。違いは、ロジックテストは、クラスとシンボルが組み込まれた独自のターゲットをビルドすることです。結果の実行可能ファイルは、シミュレータで実行でき、テスト出力をXcodeにレポートします。一方、アプリケーションテストでは、実行時にアプリに挿入されるコードにリンクする動的ライブラリを構築します。これにより、iPhone環境でテストを実行し、Xibの読み込みなどをテストできます。

ソースファイルのリンクを解除すると、テストターゲットにシンボルが表示されないため、古いプロジェクトには、アプリケーション(ユニット)テストではなく、ロジックテスト用に設定されたテストターゲットがあるようです。

最近はXcodeが 2つを区別しないようにしています のようで、デフォルトではApplication Testsターゲットを作成することで、Logic Test Targetを単体テストに変更するために変更する必要があるすべてのことを確認できます1。

また、方向が少し異なるため、静的ライブラリターゲットではなくアプリケーションターゲットがあると仮定します。

  1. テストターゲットのビルド設定で、「Bundle Loader」および「Test Host」ビルド設定を削除します。これらを後で追加するためにXcodeを取得します
  2. テストターゲットからアプリケーションからすべての.mファイルを削除する必要があります。これを行うには、すべての.mファイルを選択し、Xcodeファイルインスペクターでテストターゲットを削除するか、テストターゲットのソースのコンパイルビルドフェーズを使用します。
  3. テストターゲットの「フレームワーク検索パス」を変更します。 Xcode 5の場合、それらはその順序で$(SDKROOT)/Developer/Library/Frameworks $(inherited) $(DEVELOPER_FRAMEWORKS_DIR)である必要があり、 余分な引用符やバックスラッシュなし
  4. テストターゲットのビルド設定の[全般]ペインに移動し、ドロップダウンメニューからターゲットを選択します。メニューがすでにアプリケーションターゲットを指定している場合、それをオフとオンに切り替える必要があります。これにより、Xcodeはバンドルローダーとテストホストの設定を正しい値で再構成します。
  5. 最後に、アプリケーションのスキームを再確認してください。スキームのドロップダウンで、スキームの編集を選択します。次に、テストアクションをクリックします。テスト対象が情報ペインのリストにあることを確認し、すべてのテストが選択されていることを確認してください。

この情報は多かれ少なかれ上記のリンクされたドキュメントから来ていますが、Xcode 5の手順を更新しました。

編集:

うーん、100%eph515がデバッグシンボルが表示されることについて言っていることに注意してください。しかし、誰かがあなたのスキームのテストアクションをReleaseまたは他の構成でビルドするように設定しなかったことを確認したいかもしれません。スキームセレクタをクリックして、編集スキームを選択します。テストアクションをクリックし、ビルド構成がDebugであることを確認します

build configuration screen for test action in a scheme

静的ライブラリターゲットがある場合

静的ライブラリターゲットがある場合、2つのオプションがあります。1.ロジックテスト2.ホストアプリでのアプリケーションテスト

1.では、Bundle LoaderおよびTest Hostが静的ライブラリターゲットに対して空であることを確認する必要があります。ソースは、実行する他の方法がないため、テストターゲットにコンパイルする必要があります。

2.の場合、Xcodeで新しいアプリプロジェクトを作成し、静的ライブラリプロジェクトをサブプロジェクトとして追加する必要があります。次に、Bundle LoaderおよびTest Hostビルド設定を、新しいアプリのテストターゲットから静的ライブラリテストターゲットに手動でコピーする必要があります。次に、新しいテストアプリのスキームを開き、テストターゲットを新しいアプリのテストアクションに追加します。 libでテストを実行するには、ホストアプリのテストアクションを実行します。

47
jackslash

Xcode 6では、テストターゲット>一般>テストで「ホストアプリケーションAPIのテストを許可する」をチェックすることで、この問題を修正できました。

Xcode Screenshot

20
yood

私もこれに遭遇し、jackslashの推奨に従いましたが、もう1つ追加しました。メインターゲットを選択し、デフォルトで非表示のシンボル(Apple LVM 5.0-コード生成)を探します。いいえ。これは、単体テストのターゲットが探しているコンパイル済みソースのすべてのシンボルを「隠す」ようです。私のために働く。ジャックスラッシュで概説されているすべてのステップも必ず含めてください。

18
eph515

答えは、ジャックスラッシュとeph515の答えの組み合わせでした。

Eph515の答えのようにsymbols hidden by defaultは、デバッグ用にNoにする必要があります。

enter image description here

また、deployment postprocessingは、デバッグ用にNoにする必要があります。

enter image description here

また、テストターゲットに含まれるすべてのライブラリを単体テストから削除する必要があります。残す必要があるのは、スクリーンショットの3と、単体テストに固有のすべてです。

enter image description here

また、リストの最後にビルドスクリプトのビルドビルドフェーズがある場合は、それを削除する必要があります(ユニットテストの成果物であるため)。

次に、すべてを jackslashの答え で行います。

9
Robert

私の場合、Xcode 6.2では、プロジェクトターゲットとテストターゲットの異なるアーキテクチャでエラーが発生しました。

プロジェクトターゲットには、armv7およびarmv7sアーキテクチャのみがあります(古いライブラリがあるため)

Project Testsターゲットには、armv7、armv7s、およびarm64アーキテクチャがあります。

私の場合、arm64アーキテクチャを削除することでこの問題を解決できます。

Project Editor -> Project Tests target -> Build Settings -> Valid Architectures = armv7 armv7s

(おそらく$(ARCHS_STANDARD)の代わりに "Architectures"を$(ARCHS_STANDARD_32_BIT)に設定する必要もあります)

0
Lukáš Mareda

私にとっては、スキームにテストターゲットが追加されていないということです。

アプリターゲットの場合は、[スキームの編集]に移動し、右側の[テスト]をクリックして、下部の[+]ボタンでテストターゲットを追加します。 enter image description here

0
richy