33

受到这个问题的解决方案的启发,我尝试使用与 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();
    }
}
4

6 回答 6

44
于 2013-10-15T16:59:04.763 回答
3

因为您必须在 testSuiteDidStop 方法中创建一个新的 XCTestSuiteRun 实例,所以您不会在 == 检查中获得正确的结果。我们不依赖于实例相等性,而是使用一个简单的计数器并在它达到零时调用flush,它会在顶级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

需要一个额外的步骤,因为当包含在测试目标中时,没有对观察者进行 +initialize 调用。

在 AppDelegate 中,添加以下内容:

#ifdef DEBUG
+(void) initialize {
    if([self class] == [AppDelegate class]) {
        [[NSUserDefaults standardUserDefaults] setValue:@"GCovrTestObserver"
                                                 forKey:@"XCTestObserverClass"];
    }
}
#endif
于 2013-10-02T22:18:37.730 回答
1

这是另一个避免编辑 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

XCTOverageFlusher.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
于 2014-01-11T06:17:05.003 回答
0

-(void)applicationWillTerminate 中的 GCOV Flush 对我不起作用,我认为是因为我的应用程序在后台运行。

我还设置了 'Generate Test Coverage Files=YES' 和 'Instrument Program Flow=YES' 但没有 gcda-Files。

然后我在 TestClass 的 -(void)tearDown 中执行了“__gcov_flush()”,这为我的 TestClass 提供了 gcda-Files ;)

然后我在我的 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

我在我的 -(void)tearDown 中调用了 [AppDelegate gcovFlush] ,瞧,有我的 gcda 文件;)

我希望这会有所帮助,再见,克里斯

于 2014-01-12T17:03:12.953 回答
0

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

我没有任何图书馆问题。“-lgov”不是必需的,您不必添加任何库。LLVM 编译器直接支持覆盖。

于 2013-10-02T13:37:07.627 回答
0

如果您使用的是 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

应用委托:

- (void)applicationWillTerminate:(UIApplication *)application
{
#ifdef DEBUG
  extern void __gcov_flush(void);
  __gcov_flush();
#endif
}

然后,您需要通过在主方案的 Run 部分中将环境变量设置为来启用自定义报告子类SPECTA_REPORTER_CLASSMyReporter

于 2013-11-28T13:25:04.663 回答