2017-01-03 2 views
2

Я хотел бы использовать модульные тесты XCTest, но на самом деле не хочу использовать XCode для этого. Не понятно, как это сделать, и мне интересно, возможно ли это?Можно ли использовать модульные тесты XCTest без Xcode?

Из того, что я нашел до сих пор, можно создать впечатление, что XCTest полностью зависит от XCode. Я нашел поддержку командной строки xcodebuild test, но это зависит от поиска проекта XCode или рабочей области.

Есть ли у меня какие-либо опции, или я просто вырву из существующего кода SenTestingKit и вернусь к некоторому тестовому коду для домашнего использования? У меня есть такой код, но это не правильная вещь.


Обоснование/история:

Это не только я стар-Skool. У меня есть программа Objective-C, которую я последний раз коснулся два года назад, для которой я разработал разумный набор модульных тестов на основе SenTestingKit. Теперь я вернусь к этому коду - мне, по крайней мере, придется перестроить вещь из-за вмешательства в библиотечные изменения - я обнаружил, что SenTestingKit исчез, чтобы заменить XCTest. Ну ....

Этот код не был разработан с использованием XCode, так что не .project файл, связанный с ним, и испытания были до сих пор счастливо управлять с помощью основных программ SenTestingKit, и файл сборки check цель (это отчасти старомодно, опять-таки, отчасти отсутствие привязанности к 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

Спасибо @ 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 (это основная программа, ч Makefile, на самом деле вызывает для выполнения всех тестов):

#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 (который, следовательно, должен быть перенаправлен в другом месте) - это не представляется возможным, чтобы избежать этого и отправлять их только к моему центр наблюдения. В моей реальной программе я заменил NSLog звонки в runtests.m с функцией, которая затрагивает stdout, что я мог бы отличить от болтовни, идущей к ObserverCenter по умолчанию.
  • Смотрите также overview documentation (предполагает, что вы используете XCode),
  • ... XCTest API documentation,
  • ... и ноты в заголовках файлов на (например) /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Headers
Смежные вопросы