2017-01-03 141 views
2

我想實現XCTest單元測試,但並不真的想用XCode來做到這一點。如何做到這一點並不明顯,我想知道這是否有可能?是否可以在不使用Xcode的情況下使用XCTest單元測試?

從我到目前爲止發現的情況來看,可以得到XCTest完全依賴於XCode的印象。我發現了xcodebuild test命令行支持,但這取決於找到XCode項目或工作區。

我在這裏有任何選擇,或者我只是翻出現有的SenTestingKit代碼並恢復到一些自制的單元測試代碼?我有一些這樣的代碼需要處理,但這不是正確的事情。


理由/歷史:

這不只是我作爲老斯庫爾。我有一個Objective-C程序,這是我兩年前最後碰到的,爲此我開發了一套基於SenTestingKit的合理的單元測試。現在我回到這個代碼 - 我可能至少不得不重建這個東西,因爲介入庫變化 - 我發現SenTestingKit已經消失了,被XCTest取代。哦....

使用的XCode此代碼是沒有發展起來,所以沒有與之關聯的.project文件,並且測試到現在愉快地使用SenTestingKit的主要項目管理,以及一個Makefile check目標(部分原因在於舊的skool,部分原因是缺乏對IDE的鐘愛,部分原因是這是對Objective-C的一次實驗,因此最初堅持我所知道的)。

回答

1

如果您正在尋找基於Xcode的解決方案,請參閱this及其鏈接解決方案的示例。

有關完整的非基於Xcode的解決方案,請繼續閱讀。

幾年前,我曾問過類似的答案:Is there any non-Xcode-based command line unit testing tool for Objective-C?但自那時起事情發生了變化。

隨着時間的推移,XCTest中出現的一個有趣功能是運行自定義測試套件的能力。我曾經成功地實現他們對我研究的需要,這裏有一個例子代碼,是一個命令行的Mac OS的應用程序:

@interface FooTest : XCTestCase 
@end 

@implementation FooTest 
- (void)testFoo { 
    XCTAssert(YES); 
} 
- (void)testFoo2 { 
    XCTAssert(NO); 
} 

@end 

@interface TestObserver : NSObject <XCTestObservation> 
@property (assign, nonatomic) NSUInteger testsFailed; 
@end 

@implementation TestObserver 

- (instancetype)init { 
    self = [super init]; 

    self.testsFailed = 0; 

    return self; 
} 

- (void)testBundleWillStart:(NSBundle *)testBundle { 
    NSLog(@"testBundleWillStart: %@", testBundle); 
} 

- (void)testBundleDidFinish:(NSBundle *)testBundle { 
    NSLog(@"testBundleDidFinish: %@", testBundle); 
} 

- (void)testSuiteWillStart:(XCTestSuite *)testSuite { 
    NSLog(@"testSuiteWillStart: %@", testSuite); 
} 

- (void)testCaseWillStart:(XCTestCase *)testCase { 
    NSLog(@"testCaseWillStart: %@", testCase); 
} 

- (void)testSuiteDidFinish:(XCTestSuite *)testSuite { 
    NSLog(@"testSuiteDidFinish: %@", testSuite); 
} 

- (void)testSuite:(XCTestSuite *)testSuite didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber { 
    NSLog(@"testSuite:didFailWithDescription:inFile:atLine: %@ %@ %@ %tu", 
     testSuite, description, filePath, lineNumber); 
} 

- (void)testCase:(XCTestCase *)testCase didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber { 
    NSLog(@"testCase:didFailWithDescription:inFile:atLine: %@ %@ %@ %tu", 
     testCase, description, filePath, lineNumber); 
    self.testsFailed++; 
} 

- (void)testCaseDidFinish:(XCTestCase *)testCase { 
    NSLog(@"testCaseWillFinish: %@", testCase); 
} 

@end 

int RunXCTests() { 
    XCTestObserver *testObserver = [XCTestObserver new]; 

    XCTestObservationCenter *center = [XCTestObservationCenter sharedTestObservationCenter]; 
    [center addTestObserver:testObserver]; 

    XCTestSuite *suite = [XCTestSuite defaultTestSuite]; 

    [suite runTest]; 

    NSLog(@"RunXCTests: tests failed: %tu", testObserver.testsFailed); 

    if (testObserver.testsFailed > 0) { 
    return 1; 
    } 

    return 0; 
} 

要編譯這樣的代碼,你將需要出示文件夾的路徑,其中XCTest位於類似於:

# in your Makefile 
clang -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks XCTestDriver.m 

不要指望代碼編譯,但它應該給你一個想法。隨意問你是否有任何問題。也請按照XCTest框架的標題來了解更多關於它的類和它們的文檔。

+0

Thanks @ stanislaw-pankevich - 看起來非常有用。我仍然在通過頭文件中的文檔和文檔工作,但可能會返回問題。有了這個指針,我也發現[XCTest文檔](https://developer.apple.com/reference/xctest),並且正在抓我的腦袋...... –

1

謝謝,@ stanislaw-pankevich,一個很好的答案。在這裏,爲了完整起見,我包括了(或多或少)完成的測試程序,其中包括一些額外的細節和評論。

(這是從我的角度來看一個完整的程序,因爲它在測試定義util.h功能 ,這是不包括在內)

文件UtilTest.h

#import <XCTest/XCTest.h> 
@interface UtilTest : XCTestCase 
@end 

文件UtilTest.m

#import "UtilTest.h" 
#import "../util.h" // the definition of the functions being tested 

@implementation UtilTest 

// We could add methods setUp and tearDown here. 
// Every no-arg method which starts test... is included as a test-case. 

- (void)testPathCanonicalization 
{ 
    XCTAssertEqualObjects(canonicalisePath("/p1/./p2///p3/..//f3"), @"/p1/p2/f3"); 
} 
@end 

驅動程序runtests.m(這是主程序,它是小時生成文件實際上調用運行所有測試):

#import "UtilTest.h" 
#import <XCTest/XCTestObservationCenter.h> 

// Define my Observation object -- I only have to do this in one place 
@interface BrownieTestObservation : NSObject<XCTestObservation> 
@property (assign, nonatomic) NSUInteger testsFailed; 
@property (assign, nonatomic) NSUInteger testsCalled; 
@end 

@implementation BrownieTestObservation 

- (instancetype)init { 
    self = [super init]; 
    self.testsFailed = 0; 
    return self; 
} 

// We can add various other functions here, to be informed about 
// various events: see XCTestObservation at 
// https://developer.apple.com/reference/xctest?language=objc 
- (void)testSuiteWillStart:(XCTestSuite *)testSuite { 
    NSLog(@"suite %@...", [testSuite name]); 
    self.testsCalled = 0; 
} 

- (void)testSuiteDidFinish:(XCTestSuite *)testSuite { 
    NSLog(@"...suite %@ (%tu tests)", [testSuite name], self.testsCalled); 
} 

- (void)testCaseWillStart:(XCTestSuite *)testCase { 
    NSLog(@" test case: %@", [testCase name]); 
    self.testsCalled++; 
} 

- (void)testCase:(XCTestCase *)testCase didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber { 
    NSLog(@" FAILED: %@, %@ (%@:%tu)", testCase, description, filePath, lineNumber); 
    self.testsFailed++; 
} 

@end 

int main(int argc, char** argv) { 
    XCTestObservationCenter *center = [XCTestObservationCenter sharedTestObservationCenter]; 
    BrownieTestObservation *observer = [BrownieTestObservation new]; 
    [center addTestObserver:observer]; 

    Class classes[] = { [UtilTest class], }; // add other classes here 
    int nclasses = sizeof(classes)/sizeof(classes[0]); 

    for (int i=0; i<nclasses; i++) { 
     XCTestSuite *suite = [XCTestSuite testSuiteForTestCaseClass:classes[i]]; 
     [suite runTest]; 
    } 

    int rval = 0; 
    if (observer.testsFailed > 0) { 
     NSLog(@"runtests: %tu failures", observer.testsFailed); 
     rval = 1; 
    } 

    return rval; 
} 

的Makefile:

FRAMEWORKS=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks 
TESTCASES=UtilTest 

%.o: %.m 
    clang -F$(FRAMEWORKS) -c $< 

check: runtests 
    ./runtests 2>runtests.stderr 

runtests: runtests.o $(TESTCASES:=.o) ../libmylib.a 
    cc -o [email protected] $< -framework Cocoa -F$(FRAMEWORKS) -rpath $(FRAMEWORKS) \ 
     -framework XCTest $(TESTCASES:=.o) -L.. -lmylib 

注:

  • XCTestObserver類現在已經過時,並通過XCTestObservation取代。
  • 的測試結果發送到共享 XCTestObservationCenter,不幸的是發狂似的喋喋不休到stderr(因此必須被重定向到其他地方) - 它似乎並沒有能夠避免,並讓他們發送我觀察中心。在我的實際程序中,我用runtests.m中的NSLog調用替換了一個聊天者到stdout的函數,因此我可以從聊天記錄中區分出默認的ObservationCenter。
  • 又見overview documentation (假設您正在使用的XCode),
  • ...的XCTest API documentation
  • ...,並在(例如)在文件的報頭中的注意事項/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers
相關問題