web-dev-qa-db-ja.com

Xcode 6のXCTestを使用した単体テストの注文

Xcodeにユニットテストを指定された順序で実行するように指示する方法があるかどうか知りたいのですが。つまり、同じXCTestCaseクラスファイルではなく、すべてのクラスファイル間です。

enter image description here

たとえば、SitchozrSDKMessageTestを実行する前にSitchozrSDKSessionTestを実行したいと思います。

スタックまたはAppleドキュメントでいくつかのスレッドを調べましたが、役立つものは見つかりませんでした。

ご協力いただきありがとうございます。

32
BoilingLime

すべてがアルファベット順にソートされています(クラスとメソッド)。したがって、最後に実行するテストが必要な場合は、クラスまたはメソッドの名前を変更します((厄介な)例の場合は、「z_」のプレフィックスを付けます)。

だから...あなたはクラス名を持っています:

MyAppTest1
  -testMethodA
  -testMethodB
MyAppTest2
  -testMethodC
  -testMethodD

そして、この順序で実行されます。 MyAppTest1を2番目として実行する必要がある場合は、名前を変更して、名前がMyAppTest2(z_MyAppTest1)の後にアルファベット順になり、実行されるようにします(メソッドと同じ)。

MyAppTest2
  -a_testMethodD
  -testMethodC
z_MyAppTest1
  -testMethodA
  -testMethodB

また、例として名前を付けてください:)

27
JakubKnejzlik

現在(Xcode 7以降)、XCTestCaseメソッドはアルファベット順に実行されるため、わかりやすい名前を付けることで強制的に順序を付けることができます。ただし、これは実装の詳細であり、変更される可能性があるようです。

(うまくいけば)脆弱性を減らす方法は、+[XCTestCase testInvocations]をオーバーライドして、テストを実行する順序で独自のNSInvocationオブジェクトを返すことです。

何かのようなもの:

+ (NSArray *)testInvocations
{
    NSArray *selectorStrings = @[@"testFirstThing",
                                 @"testSecondThing",
                                 @"testAnotherThing",
                                 @"testLastThing"];

    NSMutableArray *result = [NSMutableArray array];
    for (NSString *selectorString in selectorStrings) {
        SEL selector = NSSelectorFromString(selectorString);
        NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:selector];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        invocation.selector = selector;
        [result addObject:invocation];
    }
    return result;
}

もちろん、ここでの欠点は、各テストメソッドを自動的に取得するのではなく、手動で追加する必要があることです。この状況を改善する方法はいくつかあります。 +testInvocationsのオーバーライドで、すべてのテストメソッドではなくsomeのテストメソッドのみを注文する必要がある場合は、superに呼び出し、手動で行ったメソッドを除外できます。注文してから、残りを配列の最後に貼り付けます。すべてのテストメソッドを注文する必要がある場合でも、superへの呼び出しの結果を取得し、自動的に取得されたすべてのメソッドが手動で作成された注文結果でカバーされていることを確認できます。そうでない場合は、アサートすることができ、メソッドの追加を忘れた場合は失敗します。

特定の順序で実行する必要のあるテストを作成することが「正しい」かどうかについては、議論を省きます。それが理にかなっているまれなシナリオはあると思いますが、他の人は反対するかもしれません。

15
Andrew Madsen

関数名での順序は文字の順序であり、コード内での順序は関係ありません。例えば。:

-(void)testCFun(){};
-(void)testB2Fun(){};
-(void)testB1Fun(){};

実際の実行順序は次のとおりです。

  1. testB1Funが呼び出されました
  2. testB2Funが呼び出されました
  3. testCFunが呼び出されました
7
Jixian Wang

andrew-madsen の回答に加えて:
「スマート」testInvocationsクラスメソッドを作成しました。これは、「test」で始まるセレクターを検索し、アルファベット順に並べ替えます。
したがって、セレクタ名のNSArrayを個別に管理する必要はありません。

#import <objc/runtime.h>

+ (NSArray <NSInvocation *> *)testInvocations
{
    // Get the selectors of this class
    unsigned int mc = 0;
    Method *mlist = class_copyMethodList(self.class, &mc);
    NSMutableArray *selectorNames = [NSMutableArray array];
    for (int i = 0; i < mc; i++) {
        NSString *name = [NSString stringWithFormat:@"%s", sel_getName(method_getName(mlist[i]))];
        if (name.length > 4
            && [[name substringToIndex:4] isEqualToString:@"test"]) {
            [selectorNames addObject:name];
        }
    }

    // Sort them alphabetically
    [selectorNames sortUsingComparator:^NSComparisonResult(NSString * _Nonnull sel1, NSString * _Nonnull sel2) {
        return [sel1 compare:sel2];
    }];

    // Build the NSArray with NSInvocations
    NSMutableArray *result = [NSMutableArray array];
    for (NSString *selectorString in selectorNames) {
        SEL selector = NSSelectorFromString(selectorString);
        NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:selector];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        invocation.selector = selector;
        [result addObject:invocation];
    }
    return result;
}

脚注:単体テストを互いに依存させることは悪い習慣だと考えられています

6
basvk

たとえば、プロジェクトのテストファイルの名前を次のように変更できます。

SitchozrSDKSessionTest -> t001_SitchozrSDKSessionTest
SitchozrSDKMessageTest -> t002_SitchozrSDKMessageTest

Xcodeはアルファベット順にファイルを扱います。

3
ant_one

Xcode 7以降、XCTestCaseサブクラスはアルファベット順になっています。テストメソッド自体もアルファベット順に実行されるようにするには、testInvocationsクラスメソッドをオーバーライドして、セレクターでソートされた呼び出しを返す必要があります。

@interface MyTestCase : XCTestCase
@end

@implementation MyTestCase

+ (NSArray<NSInvocation *> *) testInvocations
{
    return [[super testInvocations] sortedArrayUsingComparator:^NSComparisonResult(NSInvocation *invocation1, NSInvocation *invocation2) {
        return [NSStringFromSelector(invocation1.selector) compare:NSStringFromSelector(invocation2.selector)];
    }];
}

- (void) test_01
{
}

- (void) test_02
{
}

@end
2
0xced

OPは、CIが利用可能かどうか、またはコマンドラインの回答で十分かどうかについては触れていません。そのような場合、私はxctool [1]の使用を楽しんだ。単一のテストメソッドまでテストを実行する構文があります。

path/to/xctool.sh \
  -workspace YourWorkspace.xcworkspace \
  -scheme YourScheme \
  test -only SomeTestTarget:SomeTestClass/testSomeMethod

最初に最も簡単だと思う項目をテストするために、これを行います。

1. [] facebook/xctool:iOSまたはOSXアプリのビルドとテストを容易にするAppleのxcodebuildの代替品。 ; ; https://github.com/facebook/xctool

1
AnneTheAgile