2017-01-03 5 views
2

Ich würde gerne XCTest Unit-Tests implementieren, aber nicht wirklich XCode dafür verwenden wollen. Es ist nicht klar, wie das geht, und ich frage mich, ob das überhaupt möglich ist.Ist es möglich, XCTest Unit Tests ohne Xcode zu verwenden?

Von dem, was ich bisher gefunden habe, könnte man den Eindruck bekommen, dass XCTest vollständig von XCode abhängig ist. Ich habe die xcodebuild test Befehlszeilenunterstützung gefunden, aber das hängt davon ab, ein XCode-Projekt oder Arbeitsbereich zu finden.

Habe ich irgendwelche Optionen hier, oder reiße ich einfach den vorhandenen SenTestingKit Code aus und kehre zu irgendeinem Hausbrüheinheittestcode zurück? Ich habe einen solchen Code zur Hand, aber es ist nicht die richtige Sache zu tun.


Rationale/Geschichte:

Dies ist nicht nur ich die alte Schule zu sein. Ich habe ein Objective-C-Programm, das ich vor zwei Jahren zum letzten Mal berührt habe und für das ich eine vernünftige Reihe von Komponententests basierend auf SenTestingKit entwickelt habe. Jetzt komme ich wieder auf diesen Code zurück - ich muss das Ding wegen der zwischenzeitlichen Änderungen an der Bibliothek zumindest neu aufbauen - ich stelle fest, dass SenTestingKit verschwunden ist, um durch XCTest ersetzt zu werden. Oh well ....

Dieser Code wurde nicht entwickelt XCode verwendet, so gibt es kein .project Datei zugeordnet, und die Tests wurden bisher glücklich verwaltet SenTestingKit Hauptprogramme verwenden und ein Makefile check Ziel (das ist zum Teil alt-skool, wieder mal ein Mangel an Vorliebe für IDEs, und zum Teil war das ein Experiment mit Objective-C, also ursprünglich mit dem, was ich weiß, zusammenhängt).

Antwort

1

Wenn Sie nach Xcode-basierten Lösungen suchen, siehe this und die verknüpften Lösungen für Beispiele.

Für eine vollständige Nicht-Xcode-basierte Lösung weiterlesen.

Früher habe ich ähnliche Antwort vor ein paar Jahren gefragt: Is there any non-Xcode-based command line unit testing tool for Objective-C? aber Dinge haben sich seitdem geändert.

Ein interessantes Feature, das im Laufe der Zeit in XCTest erschien, ist die Fähigkeit, Ihre eigenen Test-Suites auszuführen. Ich habe sich für meinen Forschungsbedarf erfolgreich umzusetzen, ist hier ein Beispiel-Code, der eine Befehlszeile Mac OS-Anwendung ist:

@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; 
} 

Um diese Art von Code kompilieren Sie einen Pfad zu dem Ordner, in dem XCTest zeigen müssen ist so etwas wie:

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

Erwarten Sie nicht, dass der Code kompiliert, aber es sollte Ihnen eine Idee geben. Fühlen Sie sich frei zu fragen, wenn Sie irgendwelche Fragen haben. Folgen Sie auch den Überschriften des XCTest-Frameworks, um mehr über seine Klassen und deren Dokumente zu erfahren.

+0

Danke @ stanislaw-pankevich - das sieht sehr nützlich aus. Ich arbeite immer noch durch das und die Dokumente in den Header-Dateien, kann aber mit Fragen zurückkehren. Mit diesem Zeiger habe ich auch [die XCTest-Dokumente] (https://developer.apple.com/reference/xctest) gefunden, und ich kratze mich am Kopf über diese ... –

1

Danke, @ stanislaw-pankevich, für eine gute Antwort. Hier, zur Vollständigkeit, schließe ich (mehr oder weniger) das komplette Testprogramm ein, mit dem ich endete, das ein paar zusätzliche Details und Kommentare enthält.

(Dies ist ein komplettes Programm aus meiner Sicht, da es Funktionen testet in util.h definiert, die hier nicht enthalten ist)

Datei UtilTest.h:

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

Datei 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 

Treiberprogramm runtests.m (dies ist das Hauptprogramm, das h die Make-Datei ruft tatsächlich alle Tests laufen):

#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 

Hinweise:

  • Die XCTestObserver Klasse jetzt veraltet ist, und durch XCTestObservation ersetzt.
  • Die Ergebnisse der Tests sind ein geteilt gesendet XCTestObservationCenter, die leider distractingly zu stderr Chatter (die deshalb an anderer Stelle umgeleitet werden muss) - es ist nicht möglich scheint, dass zu vermeiden und sie schickte meine einzige zu Beobachtungszentrum stattdessen. In meinem eigentlichen Programm habe ich die NSLog Anrufe in runtests.m durch eine Funktion ersetzt, die zu stdout chattert, was ich daher vom Chatter zum Standard-ObservationCenter unterscheiden könnte.
  • Siehe overview documentation (setzt voraus, dass Sie XCode verwenden) auch
  • ... die XCTest API documentation,
  • ... und die Hinweise in den Header der Dateien (zB) /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers
Verwandte Themen