由灵感解决这个问题,我使用带有XCTest同样的方法尝试。
我设置“生成测试覆盖率文件= YES”和“仪器程序流量= YES”。
的XCode仍然不产生任何gcda文件。 任何人对如何解决这个任何想法?
码:
#import <XCTest/XCTestLog.h>
@interface VATestObserver : XCTestLog
@end
static id mainSuite = nil;
@implementation VATestObserver
+ (void)initialize {
[[NSUserDefaults standardUserDefaults] setValue:@"VATestObserver"
forKey:XCTestObserverClassKey];
[super initialize];
}
- (void)testSuiteDidStart:(XCTestRun *)testRun {
[super testSuiteDidStart:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
if (mainSuite == nil) {
mainSuite = suite;
}
}
- (void)testSuiteDidStop:(XCTestRun *)testRun {
[super testSuiteDidStop:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
if (mainSuite == suite) {
UIApplication* application = [UIApplication sharedApplication];
[application.delegate applicationWillTerminate:application];
}
}
@end
在AppDelegate.m我有:
extern void __gcov_flush(void);
- (void)applicationWillTerminate:(UIApplication *)application {
__gcov_flush();
}
编辑 :我编辑的问题,以反映当前的状态(不红鲱鱼)。
编辑要使其工作,我不得不在测试中添加的所有文件复制到测试目标包括VATestObserver。
AppDelegate.m
#ifdef DEBUG
+ (void)initialize {
if([self class] == [AppDelegate class]) {
[[NSUserDefaults standardUserDefaults] setValue:@"VATestObserver"
forKey:@"XCTestObserverClass"];
}
}
#endif
VATestObserver.m
#import <XCTest/XCTestLog.h>
#import <XCTest/XCTestSuiteRun.h>
#import <XCTest/XCTest.h>
// Workaround for XCode 5 bug where __gcov_flush is not called properly when Test Coverage flags are set
@interface VATestObserver : XCTestLog
@end
#ifdef DEBUG
extern void __gcov_flush(void);
#endif
static NSUInteger sTestCounter = 0;
static id mainSuite = nil;
@implementation VATestObserver
+ (void)initialize {
[[NSUserDefaults standardUserDefaults] setValue:@"VATestObserver"
forKey:XCTestObserverClassKey];
[super initialize];
}
- (void)testSuiteDidStart:(XCTestRun *)testRun {
[super testSuiteDidStart:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
sTestCounter++;
if (mainSuite == nil) {
mainSuite = suite;
}
}
- (void)testSuiteDidStop:(XCTestRun *)testRun {
sTestCounter--;
[super testSuiteDidStop:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
if (sTestCounter == 0) {
__gcov_flush();
}
}
更新1:
阅读更多一点关于此之后,两件事情现在已经变得很清楚,我(强调):
测试和测试应用程序被单独编译。 测试实际上注入运行的应用程序,所以__gcov_flush()
必须被调用不能测试中的应用程序中 。
- Xcode5代码覆盖率(从CMD行的CI构建) -堆栈溢出
和,
还是那句话:注射是复杂的。 你带走应该是: 不要从您的应用程序添加.m文件到您的测试目标。 你会得到意想不到的行为。
- 测试视图控制器- #1 -打火机视图控制器
下面的代码被修改,以反映这两种见解...
更新2:
增加了关于如何使这项工作,为静态库,按要求信息@MdaG中的注释。 图书馆的主要变化是:
解
这里的其他答案已经在我的项目建立的代码覆盖率极大地帮助了我。 在探索他们,我相信我已经成功地简化了代码,修正了不少。
考虑中的任何一个:
-
ExampleApp.xcodeproj
从头创建为一个“空白应用程序” -
ExampleLibrary.xcodeproj
创建为一个独立的“可可触摸静态库”
这些都是我带,以使在Xcode 5的代码覆盖率一代的步骤:
创建GcovTestObserver.m
用下面的代码文件中,ExampleAppTests组内:
#import <XCTest/XCTestObserver.h> @interface GcovTestObserver : XCTestObserver @end @implementation GcovTestObserver - (void)stopObserving { [super stopObserving]; UIApplication* application = [UIApplication sharedApplication]; [application.delegate applicationWillTerminate:application]; } @end
在做一个图书馆,因为没有应用程序调用,冲洗可以直接从观察者调用。 在这种情况下,将文件添加到ExampleLibraryTests组使用此代码来代替:
#import <XCTest/XCTestObserver.h> @interface GcovTestObserver : XCTestObserver @end @implementation GcovTestObserver - (void)stopObserving { [super stopObserving]; extern void __gcov_flush(void); __gcov_flush(); } @end
要注册测试观察者类,下面的代码添加到@implementation
中任一个的部分:
-
ExampleAppDelegate.m
文件,该ExampleApp中组内 -
ExampleLibrary.m
文件,该ExampleLibrary组内
#ifdef DEBUG + (void)load { [[NSUserDefaults standardUserDefaults] setValue:@"XCTestLog,GcovTestObserver" forKey:@"XCTestObserverClass"]; } #endif
此前,这个答案建议使用+initialize
方法(你仍然可以做,在应用程序的情况下),但它并不适用于图书馆工作...
在库的情况下, +initialize
将可能只有当测试调用库代码首次执行,到那时,它已经太迟了注册观察者。 使用+load
方法,在观察者登记时间总是进行,无论哪个场景。
在应用程序的情况下,以下代码添加到@implementation
所述的部分ExampleAppDelegate.m
文件时,ExampleApp中组内,以冲洗在退出该应用的覆盖文件:
- (void)applicationWillTerminate:(UIApplication *)application { #ifdef DEBUG extern void __gcov_flush(void); __gcov_flush(); #endif }
启用Generate Test Coverage Files
和Instrument Program Flow
将它们设置为YES
在项目构建设置(无论是“示例”和“示例测试”的目标)。
要在一个简单,一致的方式做到这一点,我添加了一个Debug.xcconfig
文件与项目的“调试”配置相关联 ,具有以下声明:
GCC_GENERATE_TEST_COVERAGE_FILES = YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES
确保所有项目.m
文件中还包括“编译源”打造“示例测试”目标的阶段。 不这样做:应用程序代码属于应用目标,测试代码属于测试目标!
运行项目的测试后,you'l能够找到生成的文件覆盖了Example.xcodeproj
在这里:
cd ~/Library/Developer/Xcode/DerivedData/
find ./Example-* -name *.gcda
笔记
步骤1
在方法声明XCTestObserver.h
表示:
/*! Sent immediately after running tests to inform the observer that it's time
to stop observing test progress. Subclasses can override this method, but
they must invoke super's implementation. */
- (void) stopObserving;
第2步
2.A)
通过创建和注册单独XCTestObserver
亚类中,我们避免具有直接与默认干涉XCTestLog
类。
内恒定的键声明XCTestObserver.h
表明这一点:
/*! Setting the XCTestObserverClass user default to the name of a subclass of
XCTestObserver indicates that XCTest should use that subclass for reporting
test results rather than the default, XCTestLog. You can specify multiple
subclasses of XCTestObserver by specifying a comma between each one, for
example @"XCTestLog,FooObserver". */
XCT_EXPORT NSString * const XCTestObserverClassKey;
2.B)
即使它使用常见的做法if(self == [ExampleAppDelegate class])
围绕里面的代码+initialize
[注:它现在使用+load
],我发现很容易忽略它在这种特殊情况下:无需调整在做复制和粘贴在正确的类名。
此外,对运行代码的保护两次是不是真的有必要在这里:这是不包含在发布版本,即使我们继承ExampleAppDelegate
有在这段代码运行一个以上的没有问题。
2.C)
在库的情况下,问题的第一个暗示来自于这个代码注释谷歌工具箱为Mac项目: GTMCodeCovereageApp.m
+ (void)load {
// Using defines and strings so that we don't have to link in XCTest here.
// Must set defaults here. If we set them in XCTest we are too late
// for the observer registration.
// (...)
而作为NSObject类参考指示:
初始化 -初始化类它接收它的第一消息之前
负荷 -每当调用类或类别被添加到Objective-C运行
该“EmptyLibrary”项目
如果有人试图通过创建自己的“EmptyLibrary”项目复制这个过程中,请记住,你需要从默认emtpy测试以某种方式调用的库代码。
如果主库类不是从测试调用时,编译器会自作聪明 ,它不会将它添加到运行时(因为它没有被任何地方调用),所以+load
方法不会被调用。
你可以简单地调用一些无害的方法(如苹果公司提出在他们的可可#类的初始化编码指南 )。 例如:
- (void)testExample
{
[ExampleLibrary self];
}
因为你在testSuiteDidStop方法来创建一个新的XCTestSuiteRun实例,你是不是要上车的==检查正确的结果。 而不是依赖于实例平等的,我们用一个简单的计数器和呼叫冲洗,当它击中零,这将在顶级XCTestSuite执行完毕。 可能有更巧妙的方法来做到这一点。
首先,我们必须设置“生成测试覆盖率文件= YES”和“仪器程序流量= YES”在测试和主要的应用程序的目标两者 。
#import <XCTest/XCTestLog.h>
#import <XCTest/XCTestSuiteRun.h>
#import <XCTest/XCTest.h>
// Workaround for XCode 5 bug where __gcov_flush is not called properly when Test Coverage flags are set
@interface GCovrTestObserver : XCTestLog
@end
#ifdef DEBUG
extern void __gcov_flush(void);
#endif
static NSUInteger sTestCounter = 0;
static id mainSuite = nil;
@implementation GCovrTestObserver
- (void)testSuiteDidStart:(XCTestRun *)testRun {
[super testSuiteDidStart:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
sTestCounter++;
if (mainSuite == nil) {
mainSuite = suite;
}
}
- (void)testSuiteDidStop:(XCTestRun *)testRun {
sTestCounter--;
[super testSuiteDidStop:testRun];
XCTestSuiteRun *suite = [[XCTestSuiteRun alloc] init];
[suite addTestRun:testRun];
if (sTestCounter == 0) {
__gcov_flush();
}
}
@end
有需要额外的步骤,因为+初始化呼叫没有被观察者包括在测试的目标时所做。
在AppDelegate中,添加以下内容:
#ifdef DEBUG
+(void) initialize {
if([self class] == [AppDelegate class]) {
[[NSUserDefaults standardUserDefaults] setValue:@"GCovrTestObserver"
forKey:@"XCTestObserverClass"];
}
}
#endif
下面是避免了编辑您的AppDelegate另一种解决方案
UIApplication的+ Instrumented.m(把这个在你的主要目标):
@implementation UIApplication (Instrumented)
#ifdef DEBUG
+ (void)load
{
NSString* key = @"XCTestObserverClass";
NSString* observers = [[NSUserDefaults standardUserDefaults] stringForKey:key];
observers = [NSString stringWithFormat:@"%@,%@", observers, @"XCTCoverageFlusher"];
[[NSUserDefaults standardUserDefaults] setValue:observers forKey:key];
}
- (void)xtc_gcov_flush
{
extern void __gcov_flush(void);
__gcov_flush();
}
#endif
@end
XCTCoverageFlusher.m(把这个在您的测试目标):
@interface XCTCoverageFlusher : XCTestObserver
@end
@implementation XCTCoverageFlusher
- (void) stopObserving
{
[super stopObserving];
UIApplication* application = [UIApplication sharedApplication];
SEL coverageFlusher = @selector(xtc_gcov_flush);
if ([application respondsToSelector:coverageFlusher])
{
objc_msgSend(application, coverageFlusher);
}
[application.delegate applicationWillTerminate:application];
}
@end
- (void)applicationWillTerminate:(UIApplication*)application
必须在应用程序的委托来定义,而不是在观察者类。
我没有任何库的问题。 “-lgov”不需要,你不添加任何库。 覆盖率由LLVM编译器的直接支持。
如果你使用Specta,因为它自身的混写这个过程是一个有点不同。 以下是为我工作:
测试包:
@interface MyReporter : SPTNestedReporter // keeps the default reporter style
@end
@implementation MyReporter
- (void) stopObserving
{
[super stopObserving];
UIApplication* application = [UIApplication sharedApplication];
[application.delegate applicationWillTerminate:application];
}
@end
AppDelegate中:
- (void)applicationWillTerminate:(UIApplication *)application
{
#ifdef DEBUG
extern void __gcov_flush(void);
__gcov_flush();
#endif
}
然后,您需要通过设置环境变量来使您的自定义子类记者SPECTA_REPORTER_CLASS
到MyReporter
在主计划的运行区段。
gcov的同花顺在 - (无效)applicationWillTerminate没有为我工作,我想是因为我的应用程序在后台运行。
我还设置“生成测试覆盖率文件= YES”和“仪器程序流量= YES”,但没有gcda档案。
然后我执行“__gcov_flush()”中 - 从识别TestClass(无效)拆卸,这给了我gcda档案为我的TestClass;)
然后,我创造了我的AppDelegate以下功能:
@interface AppDelegate : UIResponder <UIApplicationDelegate>
+(void)gcovFlush;
@end
@implementation AppDelegate
+(void)gcovFlush{
extern void __gcov_flush(void);
__gcov_flush();
NSLog(@"%s - GCOV FLUSH!", __PRETTY_FUNCTION__);
}
@end
我叫[AppDelegate中gcovFlush]在我的 - (空)拆卸,瞧,里面有我gcda文件;)
我希望这可以帮助,再见克里斯