Mda*_*daG 33 code-coverage ios7 xcode5
受到这个问题的解决方案的启发,我尝试使用与XCTest相同的方法.
我设置了'Generate Test Coverage Files = YES'和'Instrument Program Flow = 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
Run Code Online (Sandbox Code Playgroud)
在AppDelegate.m我有:
extern void __gcov_flush(void);
- (void)applicationWillTerminate:(UIApplication *)application {
__gcov_flush();
}
Run Code Online (Sandbox Code Playgroud)
编辑:我编辑了问题以反映当前状态(没有红色鲱鱼).
编辑为了使其工作,我必须将测试中的所有文件添加到测试目标,包括VATestObserver.
AppDelegate.m
#ifdef DEBUG
+ (void)initialize {
if([self class] == [AppDelegate class]) {
[[NSUserDefaults standardUserDefaults] setValue:@"VATestObserver"
forKey:@"XCTestObserverClass"];
}
}
#endif
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
Hug*_*ira 44
更新1:
在详细了解了这一点之后,我现在已经清楚了两件事情(重点补充):
测试和测试的应用程序是单独编译的.测试实际上是注入到正在运行的应用程序中,因此
__gcov_flush()必须在应用程序内部而不是在测试内部调用.
和,
再说一次:注射很复杂.您的带走应该是:不要将.m文件从您的应用程序添加到测试目标.你会得到意想不到的行为.
以下代码已更改,以反映这两个见解......
更新2:
根据评论中@MdaG的要求,添加了有关如何使静态库工作的信息.图书馆的主要变化是:
我们可以直接从-stopObserving方法中刷新,因为没有单独的应用程序在哪里注入测试.
我们必须在+load方法中注册观察者,因为在+initialize调用时(从测试套件首次访问类时),XCTest已经为时已晚.
这里的其他答案对我在项目中设置代码覆盖率有很大帮助.在探索它们时,我相信我已经设法简化了修复程序的代码.
考虑以下任何一个:
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
Run Code Online (Sandbox Code Playgroud)
在执行库时,由于没有应用程序可以调用,因此可以直接从观察者调用flush.在这种情况下,请使用以下代码将文件添加到ExampleLibraryTests组:
#import <XCTest/XCTestObserver.h>
@interface GcovTestObserver : XCTestObserver
@end
@implementation GcovTestObserver
- (void)stopObserving
{
[super stopObserving];
extern void __gcov_flush(void);
__gcov_flush();
}
@end
Run Code Online (Sandbox Code Playgroud)要注册测试观察者类,请将以下代码添加到以下@implementation任一部分:
ExampleAppDelegate.m文件,在ExampleApp组内ExampleLibrary.m文件,在ExampleLibrary组内
#ifdef DEBUG
+ (void)load {
[[NSUserDefaults standardUserDefaults] setValue:@"XCTestLog,GcovTestObserver"
forKey:@"XCTestObserverClass"];
}
#endif
Run Code Online (Sandbox Code Playgroud)
以前,这个答案建议使用这种+initialize方法(你仍然可以在应用程序的情况下这样做),但它不适用于库......
对于库,+initialize只有在测试第一次调用库代码时才会执行,然后注册观察者已经太晚了.使用该+load方法,观察者注册总是及时完成,无论哪种情况.
对于Apps,请将以下代码添加到ExampleApp组内的文件@implementation部分,以在退出应用程序时刷新coverage文件:ExampleAppDelegate.m
- (void)applicationWillTerminate:(UIApplication *)application
{
#ifdef DEBUG
extern void __gcov_flush(void);
__gcov_flush();
#endif
}
Run Code Online (Sandbox Code Playgroud)启用Generate Test Coverage Files并在项目构建设置中Instrument Program Flow设置它们YES(对于"示例"和"示例测试"目标).
为了以简单一致的方式执行此操作,我添加了与项目的"Debug"配置相关联的Debug.xcconfig文件,其中包含以下声明:
GCC_GENERATE_TEST_COVERAGE_FILES = YES
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES
Run Code Online (Sandbox Code Playgroud)确保所有项目的 不要这样做:app代码属于app目标,测试代码属于测试目标!.m文件也包含在"示例测试"目标的"编译源"构建阶段中.
在为项目运行测试之后,您将能够Example.xcodeproj在此处找到生成的覆盖文件:
cd ~/Library/Developer/Xcode/DerivedData/
find ./Example-* -name *.gcda
Run Code Online (Sandbox Code Playgroud)
里面的方法声明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;
Run Code Online (Sandbox Code Playgroud)
通过创建和注册单独的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;
Run Code Online (Sandbox Code Playgroud)
尽管常见的做法是使用[注意:它现在正在使用]中if(self == [ExampleAppDelegate class])的代码,我发现在这种特殊情况下更容易省略它:在复制和粘贴时无需调整到正确的类名.+initialize +load
此外,防止运行代码两次的保护在这里并不是必需的:这不包含在发布版本中,即使我们子类ExampleAppDelegate,运行此代码也没有问题.
就图书馆而言,问题的第一个提示来自Google Toolbox for 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.
// (...)
Run Code Online (Sandbox Code Playgroud)
并且正如NSObject类参考指示:
initialize - 在收到第一条消息之前初始化类
load - 每当将类或类别添加到Objective-C运行时时调用
如果有人试图通过创建自己的"EmptyLibrary"项目来复制此过程,请记住,您需要以某种方式从默认的emtpy测试中调用库代码.
如果未从测试中调用主库类,则编译器将尝试智能并且不会将其添加到运行时(因为它不会在任何地方调用),因此+load不会调用该方法.
您可以简单地调用一些无害的方法(正如Apple在他们的Cocoa #Class Initialization编码指南中所建议的那样).例如:
- (void)testExample
{
[ExampleLibrary self];
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15659 次 |
| 最近记录: |