【问题标题】:Ordering unit test using XCTest in Xcode 6在 Xcode 6 中使用 XCTest 对单元测试进行排序
【发布时间】:2014-11-30 17:57:11
【问题描述】:

我想知道是否有任何方法可以告诉 Xcode 以指定的顺序运行单元测试。我的意思不是在同一个 XCTestCase 类文件中,而是在所有类文件之间。

例如,我想在运行 SitchozrSDKMessageTest 之前运行 SitchozrSDKSessionTest。

我查看了堆栈或 Apple 文档上的几个线程,但没有发现任何有用的东西。

感谢您的帮助。

【问题讨论】:

  • 不是我发现的,我相信这个理论是单元测试是原子的和独立的,所以它们运行的​​顺序无关紧要。你可以通过按 play 来运行一套测试在你想要的测试文件旁边。
  • 例如这些是 API 调用的测试。对于每个呼叫,我都需要一个由特定呼叫检索的令牌。检索到令牌后,我将其设置在我的 HTTP 客户端中。我只想执行一次调用以检索令牌并且能够执行所有其他调用。我所做的“破解”它是我所有测试 API 调用的测试类继承自执行调用以检索令牌的测试类。然后在每次调用 api 之前我调用[super getToken]。它有效,但我想知道是否有其他解决方案。
  • @BoilingLime 正如其他人所提到的,每个测试都应该是原子的和独立的。您是否尝试过模拟令牌检索?
  • @BoilingLime 似乎应该在您的 setUp() 函数中完成获取令牌。

标签: ios objective-c xcode xcode6 xctest


【解决方案1】:

全部按字母顺序排序(类和方法)。因此,当您需要最后运行一些测试时,只需更改类或方法的名称(对于(讨厌的)示例,通过前缀 'z_')。

所以...你有类名:

MyAppTest1
  -testMethodA
  -testMethodB
MyAppTest2
  -testMethodC
  -testMethodD

它们按此顺序运行。如果您需要将 MyAppTest1 作为第二个运行,只需重命名,使其名称按字母顺序在 MyAppTest2 (z_MyAppTest1) 之后运行(方法相同):

MyAppTest2
  -a_testMethodD
  -testMethodC
z_MyAppTest1
  -testMethodA
  -testMethodB

另外请以命名为例:)

【讨论】:

  • 应该注意的是,这在 Xcode 8 中不再适用
  • Xcode 8.2.1:工作。请注意:你应该重命名类,而不是文件。
【解决方案2】:

确实,目前(从 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 时订购一些 测试方法,而不是全部,您可以调用super,过滤掉您手动订购的那些方法,然后将其余部分附加到您返回的数组的末尾。如果您需要订购所有测试方法,您仍然可以获得调用 super 的结果,并验证您手动创建的订购结果是否涵盖了所有自动拾取的方法。如果没有,您可以断言,如果您忘记添加任何方法,则会导致失败。

我暂且不讨论编写必须按特定顺序运行的测试是否“正确”。我认为在少数情况下这是有道理的,但其他人可能不同意。

【讨论】:

  • 这很有趣。你知道这些方法是否真的需要命名为“testXXX”吗?如果没有,可能你不需要过滤任何特殊的东西 - 只需将你的方法添加到“[super]”数组中......我有以下问题:我需要创建、处理和删除一些记录。我想衡量每个阶段的表现。但是,如果将所有这些例程放在同一个方法中,我会得到“每个测试方法只能记录一组指标”。所以我希望将它们分成3种不同的方法,但我仍然需要确保执行顺序。
  • @NickEntin:不,我认为它们不需要以“test”为前缀。 XCTestCase 使用它来自动获取其实现+testInvocations 的方法,但如果您自己提供调用,您应该可以随意命名它们。我自己还是喜欢“testXXX”约定。关于您的问题,您应该提出一个单独的问题并将其链接到此处。
  • NSInvocation 在 Swift 中不可用,有什么解决方法吗?
  • @fede1608:此方法必须返回一个 NSInvocations 数组。这就是它的工作原理。所以,我认为唯一不难看的解决方法是在 Objective-C 中实现它。您大概可以自己创建 XCTestCase 子类,并且这个方法是 ObjC,然后使用 Swift 扩展来添加实际的测试用例。
  • 使用 Xcode 8.2.1 并尝试您的解决方案,当我为测试类运行测试套件时,我看到在调用 -testInvocations 之前调用了我的一个测试方法。您在 8.2.1 中尝试过吗?
【解决方案3】:

它按函数名称字母顺序排序,无论您在代码中如何排序。 例如:

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

实际执行顺序是:

  1. 调用testB1Fun
  2. 调用 testB2Fun
  3. 调用了testCFun

【讨论】:

  • 我不想在特定的测试类文件中订购测试,而是类文件本身。
  • 这个很有用!你从哪里得到这些信息的?有记录吗?
  • 我希望 @BoilingLime 的评论能在 14 年引起关注
【解决方案4】:

除了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;
}

脚注:让单元测试相互依赖是一种不好的做法

【讨论】:

  • 这不是标准testInvocations 所做的吗?即按字母顺序对它们进行排序并运行它们?
【解决方案5】:

例如,您可以像这样重命名项目中的测试文件:

SitchozrSDKSessionTest -> t001_SitchozrSDKSessionTest
SitchozrSDKMessageTest -> t002_SitchozrSDKMessageTest

Xcode 使用字母顺序处理文件。

【讨论】:

    【解决方案6】:

    从 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
    

    【讨论】:

      【解决方案7】:

      OP 没有提到 CI 是否可用或命令行答案是否足够。在这些情况下,我很喜欢使用 xctool [1]。它具有将测试运行到单个测试方法的语法:

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

      我会这样做以确保我们首先测试我们认为最简单的项目。

      1.[] facebook/xctool:Apple 的 xcodebuild 的替代品,可以更轻松地构建和测试 iOS 或 OSX 应用程序。 ; ; https://github.com/facebook/xctool

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-28
        • 2015-01-30
        • 2014-09-02
        • 1970-01-01
        • 1970-01-01
        • 2016-07-05
        • 2016-11-08
        • 1970-01-01
        相关资源
        最近更新 更多