You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用xcodebuild的-only-testing选择testInvocations动态生成的XCTest子集?

动态生成XCTest测试方法后使用-only-testing指定单个测试的问题

问题背景

我需要在XCTestCase子类中动态生成测试方法,通过重写+(NSArray<NSInvocation*>*)testInvocations在运行时生成了example_test、permissions_location_test和permissions_many_test三个测试方法,简化代码如下:

@import XCTest;@import ObjectiveC.runtime;@interface ParametrizedTests : XCTestCase@end@implementation ParametrizedTests+ (NSArray&lt;NSInvocation *&gt; *)testInvocations {  NSLog(@"testInvocations() called");  /* Prepare dummy input */  __block NSMutableArray&lt;NSString *&gt; *dartTestFiles = [[NSMutableArray alloc] init];  [dartTestFiles addObject:@"example_test"];  [dartTestFiles addObject:@"permissions_location_test"];  [dartTestFiles addObject:@"permissions_many_test"];  NSMutableArray&lt;NSInvocation *&gt; *invocations = [[NSMutableArray alloc] init];  NSLog(@"Before the loop, %lu elements in the array", (unsigned long)dartTestFiles.count);  for (int i = 0; i &lt; dartTestFiles.count; i++) {    /* Step 1 */    NSString *name = dartTestFiles[i];    void (^anonymousFunc)(ParametrizedTests *) = ^(ParametrizedTests *instance) {      NSLog(@"anonymousFunc called!");    };    IMP implementation = imp_implementationWithBlock(anonymousFunc);    NSString *selectorStr = [NSString stringWithFormat:@"%@", name];    SEL selector = NSSelectorFromString(selectorStr);    class_addMethod(self, selector, implementation, "v@:");    /* Step 2 */    NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];    invocation.selector = selector;    NSLog(@"RunnerUITests.testInvocations(): selectorStr = %@", selectorStr);    [invocations addObject:invocation];  }  NSLog(@"After the loop");  return invocations;}@end

全量运行测试正常

使用以下命令可以一次性运行所有测试:

xcodebuild test \
  -scheme Landmarks \
  -destination 'platform=iOS Simulator,name=iPhone 15'

输出片段显示测试正常执行:

Test Suite 'Selected tests' passed at 2023-11-16 13:44:59.148.
     Executed 3 tests, with 0 failures (0 unexpected) in 0.246 (0.248) seconds

单个测试运行失败的问题

尝试使用-only-testing标志选择单个测试运行时,命令无法生效,没有测试被执行:

xcodebuild test \
  -scheme Landmarks \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  -only-testing 'LandmarksUITests/ParametrizedTests/example_test'

输出结果:

Test Suite 'ParametrizedTests' passed at 2023-11-16 13:45:58.472.
     Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds

尝试指定testInvocations方法也得到相同结果。

解决方案:可行,需调整实现方式

问题根源在于XCTest的-only-testing是在测试发现阶段筛选测试方法,而原代码仅在testInvocations(测试执行阶段)才生成方法,导致发现阶段无法识别这些动态测试。需修改实现,让XCTest在发现阶段就能找到动态生成的测试:

修改后的代码示例

@import XCTest;
@import ObjectiveC.runtime;

@interface ParametrizedTests : XCTestCase
@end

@implementation ParametrizedTests

+ (void)initialize {
    if (self == [ParametrizedTests class]) {
        // 预定义所有动态测试名称
        NSArray *dartTestFiles = @[@"example_test", @"permissions_location_test", @"permissions_many_test"];
        for (NSString *name in dartTestFiles) {
            SEL selector = NSSelectorFromString(name);
            // 避免重复添加方法
            if (![self instancesRespondToSelector:selector]) {
                void (^anonymousFunc)(ParametrizedTests *) = ^(ParametrizedTests *instance) {
                    NSLog(@"anonymousFunc called!");
                };
                IMP implementation = imp_implementationWithBlock(anonymousFunc);
                class_addMethod(self, selector, implementation, "v@:");
            }
        }
    }
}

// 重写testMethodSelectors,返回所有动态测试的selector,供XCTest发现阶段扫描
+ (NSSet<SEL> *)testMethodSelectors {
    NSSet *defaultSelectors = [super testMethodSelectors];
    NSMutableSet *dynamicSelectors = [NSMutableSet setWithSet:defaultSelectors];
    NSArray *dartTestFiles = @[@"example_test", @"permissions_location_test", @"permissions_many_test"];
    for (NSString *name in dartTestFiles) {
        [dynamicSelectors addObject:NSSelectorFromString(name)];
    }
    return dynamicSelectors;
}

+ (NSArray<NSInvocation *> *)testInvocations {
    NSLog(@"testInvocations() called");
    NSMutableArray<NSInvocation *> *invocations = [[NSMutableArray alloc] init];
    NSArray *dartTestFiles = @[@"example_test", @"permissions_location_test", @"permissions_many_test"];
    
    for (NSString *name in dartTestFiles) {
        SEL selector = NSSelectorFromString(name);
        NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        invocation.selector = selector;
        NSLog(@"RunnerUITests.testInvocations(): selectorStr = %@", name);
        [invocations addObject:invocation];
    }
    return invocations;
}

@end

关键修改点

  • 提前注册方法:在+initialize中预添加所有动态测试的方法实现,确保测试发现阶段类已响应这些selector。
  • 重写testMethodSelectors:返回包含所有动态测试selector的集合,让XCTest在发现阶段能扫描到这些测试方法,这样-only-testing就能匹配到目标测试。

验证修改效果

修改后,再次执行单个测试的命令即可正常运行:

xcodebuild test \
  -scheme Landmarks \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  -only-testing 'LandmarksUITests/ParametrizedTests/example_test'

内容的提问来源于stack exchange,提问作者Bartek Pacia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 01:10:25