+ (void)initialize { [QuickConfiguration initialize]; World *world = [World sharedWorld]; [world performWithCurrentExampleGroup:[world rootExampleGroupForSpecClass:self] closure:^{ QuickSpec *spec = [self new]; @try { [spec spec]; } @catch (NSException *exception) { [NSException raise:NSInternalInconsistencyException format:@"An exception occurred when building Quick's example groups.\n" @"Some possible reasons this might happen include:\n\n" @"- An 'expect(...).to' expectation was evaluated outside of " @"an 'it', 'context', or 'describe' block\n" @"- 'sharedExamples' was called twice with the same name\n" @"- 'itBehavesLike' was called with a name that is not registered as a shared example\n\n" @"Here's the original exception: '%@', reason: '%@', userInfo: '%@'", exception.name, exception.reason, exception.userInfo]; } [self testInvocations]; }]; }
自定义 spec
写测试的时候,你需要重写 spec 方法来自定义一组 example group 或者 example。看代码我们知道,World 单例从根 example group 开始,顺着结构树往下遍历,并对每一个 example 执行 clourse。其中,clourse 包括几个部分:
通过 World 单例保存的映射获取我们已经定义的 examples,包括 configuration 里面设置的过滤规则
遍历 examples,为每个对应的 example:通过方法签名 生成 NSInvocation 实现 XCTestCase 的消息转发(即执行测试方法),然后 hook 进 Invocation 的 setter 方法,这样就能对 current example 保持引用。从而测试失败时可以获取 current example 的相关信息如名字,行数等等