我有一个应用程序需要在其
XCTestCase
子类中动态生成测试方法。我使用 +(NSArray<NSInvocation*>*)testInvocations
在运行时动态生成具有以下(虚拟)名称的测试方法:example_test
、permissions_location_test
和 permissions_many_test
。下面是一个简化的、可直接粘贴到 Xcode 中的代码。
@import XCTest;
@import ObjectiveC.runtime;
@interface ParametrizedTests : XCTestCase
@end
@implementation ParametrizedTests
+ (NSArray<NSInvocation *> *)testInvocations {
NSLog(@"testInvocations() called");
/* Prepare dummy input */
__block NSMutableArray<NSString *> *dartTestFiles = [[NSMutableArray alloc] init];
[dartTestFiles addObject:@"example_test"];
[dartTestFiles addObject:@"permissions_location_test"];
[dartTestFiles addObject:@"permissions_many_test"];
NSMutableArray<NSInvocation *> *invocations = [[NSMutableArray alloc] init];
NSLog(@"Before the loop, %lu elements in the array", (unsigned long)dartTestFiles.count);
for (int i = 0; i < 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
我现在面临的问题是,我无法仅选择一个测试来使用
xcodebuild
的 -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
我也尝试过这样做:
xcodebuild test \
-scheme Landmarks \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-only-testing 'LandmarksUITests/ParametrizedTests/testInvocations'
但结果是一样的。
所以问题是:如何使用 -only-testing 选项选择测试子集(在运行时使用 testInvocations 动态生成)?。有可能吗?
经过一番挖掘,我发现当
-only-testing MyTarget/MyClass/myTest
通过时,XCTest 调用测试的方式不同。
我的
LandmarksTests
类缺少对NSObject.instancesRespondToSelector:aSelector:的覆盖。
@implementation ParametrizedTests
+ + (BOOL)instancesRespondToSelector:(SEL)aSelector {
+ [self defaultTestSuite]; // calls testInvocations
+ BOOL result = [super instancesRespondToSelector:aSelector];
+ return true;
+ }
+ (NSArray<NSInvocation *> *)testInvocations {
NSLog(@"testInvocations() called");
这是最终的文件,可以复制粘贴到
ParametrizedTests.m
UI 测试目标内的文件 LandmarksUITests
中,并且工作正常!
@import XCTest;
@import ObjectiveC.runtime;
@interface ParametrizedTests : XCTestCase
@end
@implementation ParametrizedTests
+ (BOOL)instancesRespondToSelector:(SEL)aSelector {
[self defaultTestSuite]; // calls testInvocations
BOOL result = [super instancesRespondToSelector:aSelector];
return true;
}
+ (NSArray<NSInvocation *> *)testInvocations {
NSLog(@"testInvocations() called");
/* Prepare dummy input */
__block NSMutableArray<NSString *> *dartTestFiles = [[NSMutableArray alloc] init];
[dartTestFiles addObject:@"example_test"];
[dartTestFiles addObject:@"permissions_location_test"];
[dartTestFiles addObject:@"permissions_many_test"];
NSMutableArray<NSInvocation *> *invocations = [[NSMutableArray alloc] init];
NSLog(@"Before the loop, %lu elements in the array", (unsigned long)dartTestFiles.count);
for (int i = 0; i < 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
另请参阅: