产生gcda-文件与Xcode5,iOS7模拟器和XCTest产生gcda-文件与Xcode5,iO

2019-05-13 02:32发布

由灵感解决这个问题,我使用带有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();
    }
}

Answer 1:

更新1:

阅读更多一点关于此之后,两件事情现在已经变得很清楚,我(强调):

测试和测试应用程序被单独编译。 测试实际上注入运行的应用程序,所以__gcov_flush()必须被调用不能测试中的应用程序中

- Xcode5代码覆盖率(从CMD行的CI构建) -堆栈溢出

和,

还是那句话:注射是复杂的。 你带走应该是: 不要从您的应用程序添加.m文件到您的测试目标。 你会得到意想不到的行为。

- 测试视图控制器- #1 -打火机视图控制器

下面的代码被修改,以反映这两种见解...


更新2:

增加了关于如何使这项工作,为静态库,按要求信息@MdaG中的注释。 图书馆的主要变化是:

  • 我们可以从直接冲洗-stopObserving因为没有一个单独的应用程序,其中注入的测试方法。

  • 我们必须注册在观察者+load ,因为当时法+initialize被称为(上课的时候首先从测试套件访问)它已经太晚XCTest把它捡起来。


这里的其他答案已经在我的项目建立的代码覆盖率极大地帮助了我。 在探索他们,我相信我已经成功地简化了代码,修正了不少。

考虑中的任何一个:

  • ExampleApp.xcodeproj从头创建为一个“空白应用程序”
  • ExampleLibrary.xcodeproj创建为一个独立的“可可触摸静态库”

这些都是我带,以使在Xcode 5的代码覆盖率一代的步骤:

  1. 创建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 
  2. 要注册测试观察者类,下面的代码添加到@implementation中任一个的部分:

    • ExampleAppDelegate.m文件,该ExampleApp中组内
    • ExampleLibrary.m文件,该ExampleLibrary组内

     #ifdef DEBUG + (void)load { [[NSUserDefaults standardUserDefaults] setValue:@"XCTestLog,GcovTestObserver" forKey:@"XCTestObserverClass"]; } #endif 

    此前,这个答案建议使用+initialize方法(你仍然可以做,在应用程序的情况下),但它并不适用于图书馆工作...

    在库的情况下, +initialize将可能只有当测试调用库代码首次执行,到那时,它已经太迟了注册观察者。 使用+load方法,在观察者登记时间总是进行,无论哪个场景。

  3. 在应用程序的情况下,以下代码添加到@implementation所述的部分ExampleAppDelegate.m文件时,ExampleApp中组内,以冲洗在退出该应用的覆盖文件:

     - (void)applicationWillTerminate:(UIApplication *)application { #ifdef DEBUG extern void __gcov_flush(void); __gcov_flush(); #endif } 
  4. 启用Generate Test Coverage FilesInstrument Program Flow将它们设置为YES在项目构建设置(无论是“示例”和“示例测试”的目标)。

    要在一个简单,一致的方式做到这一点,我添加了一个Debug.xcconfig文件与项目的“调试”配置相关联 ,具有以下声明:

     GCC_GENERATE_TEST_COVERAGE_FILES = YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES 
  5. 确保所有项目.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];
}


Answer 2:

因为你在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


Answer 3:

下面是避免了编辑您的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


Answer 4:

- (void)applicationWillTerminate:(UIApplication*)application必须在应用程序的委托来定义,而不是在观察者类。

我没有任何库的问题。 “-lgov”不需要,你不添加任何库。 覆盖率由LLVM编译器的直接支持。



Answer 5:

如果你使用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_CLASSMyReporter在主计划的运行区段。



Answer 6:

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文件;)

我希望这可以帮助,再见克里斯



文章来源: Generate gcda-files with Xcode5, iOS7 simulator and XCTest