How to generate XCTest test methods

Issue #576

Code

See Spek

Override testInvocations to specify test methods

https://developer.apple.com/documentation/xctest/xctestcase/1496271-testinvocations

Returns an array of invocations representing each test method in the test case.

Because testInvocations is unavailable in Swift, we need to use ObjC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import "include/SpekHelperTestCase.h"

@implementation SpekHelperTestCase

- (instancetype)init {
self = [super initWithInvocation: nil];
return self;
}

+ (NSArray<NSInvocation *> *)testInvocations {
NSArray<NSString *> *selectorStrings = [self spekGenerateTestMethodNames];
NSMutableArray<NSInvocation *> *invocations = [NSMutableArray arrayWithCapacity:selectorStrings.count];

for (NSString *selectorString in selectorStrings) {
SEL selector = NSSelectorFromString(selectorString);
NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;

[invocations addObject:invocation];
}

return invocations;
}

+ (NSArray<NSString *> *)spekGenerateTestMethodNames {
return @[];
}

@end

Generate test methods

Calculate based on Describe and It, and use Objc runtime class_addMethod to add instance methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
open class SpekTestCase: SpekHelperTestCase {
open class func makeDescribe() -> Describe {
return Describe("empty")
}

#if canImport(SpekHelper)

override public class func spekGenerateTestMethodNames() -> [String] {
let describe = Self.makeDescribe()

var names: [String] = []
generate(describe: describe, names: &names)
return names
}

private static func addInstanceMethod(name: String, closure: @escaping () -> Void) -> String {
let block: @convention(block) (SpekTestCase) -> Void = { spekTestCase in
let _ = spekTestCase
closure()
}

let implementation = imp_implementationWithBlock(block as Any)
let selector = NSSelectorFromString(name)
class_addMethod(self, selector, implementation, "v@:")

return name
}
}

Read more

Comments