ÔÚObjc.io #1µÄTesting View ControllersÖн²½âµÄ¾ÍÊǵ¥Ôª²âÊÔµÄÏà¹ØÄÚÈÝ¡£±¾ÎÄ˵ÏÂÈçºÎͨ¹ýXcode
5Öм¯³ÉµÄXCTest¿ò¼Ü½øÐмòµ¥µÄµ¥Ôª²âÊÔ¡£
ʲôÊǵ¥Ôª²âÊÔ
Ê×ÏÈʲôÊǵ¥Ôª²âÊÔ£¿Î¬»ù°Ù¿ÆÖеĽâÊÍÊÇ£º
ÔÚ¼ÆËã»ú±à³ÌÖУ¬µ¥Ôª²âÊÔ£¨ÓÖ³ÆÎªÄ£¿é²âÊÔ, Unit Testing£©ÊÇÕë¶Ô³ÌÐòÄ£¿é£¨Èí¼þÉè¼ÆµÄ×îСµ¥Î»£©À´½øÐÐÕýÈ·ÐÔ¼ìÑéµÄ²âÊÔ¹¤×÷¡£³ÌÐòµ¥ÔªÊÇÓ¦ÓõÄ×îС¿É²âÊÔ²¿¼þ¡£ÔÚ¹ý³Ì»¯±à³ÌÖУ¬Ò»¸öµ¥Ôª¾ÍÊǵ¥¸ö³ÌÐò¡¢º¯Êý¡¢¹ý³ÌµÈ£»¶ÔÓÚÃæÏò¶ÔÏó±à³Ì£¬×îСµ¥Ôª¾ÍÊÇ·½·¨£¬°üÀ¨»ùÀࣨ³¬Àࣩ¡¢³éÏóÀà¡¢»òÕßÅÉÉúÀࣨ×ÓÀࣩÖеķ½·¨¡£
ͨ³£À´Ëµ£¬³ÌÐòԱÿÐÞ¸ÄÒ»´Î³ÌÐò¾Í»á½øÐÐ×îÉÙÒ»´Îµ¥Ôª²âÊÔ£¬ÔÚ±àд³ÌÐòµÄ¹ý³ÌÖÐǰºóºÜ¿ÉÄÜÒª½øÐжà´Îµ¥Ôª²âÊÔ£¬ÒÔ֤ʵ³ÌÐò´ïµ½Èí¼þ¹æ¸ñÊ飨en:Specification£©ÒªÇóµÄ¹¤×÷Ä¿±ê£¬Ã»ÓгÌÐò´íÎó£»ËäÈ»µ¥Ôª²âÊÔ²»ÊÇʲô±ØÐëµÄ£¬µ«Ò²²»»µ£¬ÕâÇ£Éæµ½ÏîÄ¿¹ÜÀíµÄÕþ²ß¾ö¶¨¡£
ÓÉÓÚÎÒÃÇOC³ÌÐòԱдµÄͨ³£¶¼ÊÇÃæÏò¶ÔÏóµÄ³ÌÐò£¬ËùÒÔÎÒÃÇÔÚ½øÐе¥Ôª²âÊÔʱ£¬Í¨³£¶¼ÊÇÒÔÒ»¸ö·½·¨Îªµ¥Î»²âÊÔ£¬Ä¿µÄµ±È»ÊDZ£Ö¤Æä²»»á³ö´íÁË¡£°´ÕÕObjc.ioÎÄÕµĹ۵㣬Èç¹ûÎÒÃÇ´úÂëÖ®¼äµÄñîºÏ¶ÈºÜС£¬ÄÇô¿ÉÒÔ½«Æä·Ö½â³É¶à¸öС²¿¼þ£¬´Ó¶ø¸üÒ×ÓÚ²âÊÔ¡£
ÔÎÄ»¹Ìáµ½Ò»¸ö¸ÅÄTDD(Test-Driven Development)£¬¼´²âÊÔÇý¶¯¿ª·¢£¬¸ÃģʽҪÇ󿪷¢ÕßÔÚ±àдij¸ö¹¦ÄܵĴúÂë֮ǰÏȽ«Æä²âÊÔ´úÂëдºÃ£¬È»ºó±àдʵÏÖ´úÂë²¢½øÐвâÊÔ£¬´Ó¶ø±£Ö¤ÊµÏֵĴúÂë²»»á³öÏÖÎÊÌâ¡£Òò´ËÕû¸öÏîÄ¿µÄ¿ª·¢½ø¶È½«ÓɲâÊÔÀ´Çý¶¯£¬ÕâÓÐÖúÓÚ¿ª·¢³ö¸ßÖÊÁ¿¶øÓÖÕýÈ·µÄ´úÂ룬ʵÏÖÃô½Ý¿ª·¢¡££¨Objc.ioÉÏÃæµÄÎÄÕÂÕæµÄ·Ç³£µÄºÃ£©
ºÃ°É£¬¿ÆÆÕÍêÁË£¬ÏÂÃæ½øÈëXcode Unit TestingµÄ²¿·Ö¡£
XCTest
1.µÚÒ»¸öµ¥Ôª²âÊÔ
XCTestÊÇXcode 5ÖÐ×Ô´øµÄ²âÊÔ¿ò¼Ü£¬ËüºÍXcode 4¼°Ö®Ç°µÄSenTestKit£¬OCUnitÓÐʲôǰÒòºó¹û£¬Ð¡µÜûÓÐ×ö¶àÉÙÑо¿£¬ËùÒÔ²»ËµÁË¡£
ÏÂÃæ´ÓÒ»¸öDemo¿ªÊ¼¡£Ê×ÏÈÓÃXcodeн¨Ò»¸ö¹¤³ÌUnitTestDemo£¬¹¤³ÌĿ¼½á¹¹ÈçÏ£º

¿ÉÒÔ¿´µ½¹¤³ÌÏÂÃæ¶àÁËÒ»¸ö½ÐUnitTestDemoTestsµÄ²¿·Ö£¬TargetsÒ²¶àÁËÒ»¸öUnitTestDemoTests£¬¸ù¾Ýͼ±ê³õ²½ÈÏΪ¸ÃTargetÅܵÄÊÇÒ»¸ö¿ò¼Ü¡£
ÕâÁ½¸ö¶à³öÀ´µÄ¶«Î÷£¨Ïà±ÈXcode 4ûÓÐInclude Unit TestsµÄ¹¤³Ì£©¾ÍÊÇÓÃÀ´×öµ¥Ôª²âÊԵ쬯äÌØµãÊÇÎļþÃû»òTargetÃû¶¼ÒÔTests½áβ¡£
ÔÙÀ´¿´ÏÂUnitTestDemoTests.mÖеĴúÂ룺
#import <XCTest/XCTest.h> @interface UnitTestDemoTests : XCTestCase @end @implementation UnitTestDemoTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testExample { XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); } @end
|
¸ÃÀà¼Ì³Ð×ÔXCTestCaseÀ࣬ÆäÖаüº¬Èý¸ö·½·¨£ºsetUp£¬tearDownºÍtestExample¡£
setUp·½·¨ÓÃÓÚÔÚ²âÊÔǰÉèÖúÃÒª²âÊԵķ½·¨£¬tearDownÔòÊÇÔÚ²âÊÔºó½«ÉèÖúõÄÒª²âÊԵķ½·¨²ðжµô¡£testExample¹ËÃû˼Òå¾ÍÊÇÒ»¸öʾÀý¡£
°´¿ì½Ý¼üCommand + U½øÐе¥Ôª²âÊÔ£¬½á¹ûÈçÏ£º

¿ÉÒÔ¿´µ½Ã»ÓÐͨ¹ý²âÊÔ£¬ÔÚIssue NavigatorºÍ¿ØÖÆÌ¨¶¼Êä³öÁË´íÎóÐÅÏ¢£º±¾ÀàÖеÄtestExample·½·¨Ã»ÓÐʵÏÖ¡£
ʵ¼ÊÉÏ£¬Õâ¸ö´íÎóÊÇÎÒÃÇÖ÷¶¯Å׳öÀ´µÄ¡£XCTFailÊÇÒ»¸öºê£¬Æä×÷ÓþÍÊÇÈòâÊÔʧ°Ü£¬ºóÃæµÄNo implementation
for \"%s\"", __PRETTY_FUNCTION__¾ÍÊÇÒª±¨¸æµÄ´íÎóÐÅÏ¢£¬ÓÉÎÒÃÇ×Ô¶¨¡£
±¨´í×ÜÊÇÈÃÈ˲»Ë¬£¬ºÃ°É£¬ÎÒÃǽ«Æä×¢Ê͵ô£¬ÁíÍâдһ¸ö²âÊÔ·½·¨£¬³¢µãÌðÍ·¡£ÎªÁ˹淶£¬½¨Òéÿ¸ö²âÊÔ·½·¨¶¼Ð´³É¡°
- (void)testXXX ¡±ÐÎʽ£¬XXX±íʾҪ²âÊԵķ½·¨Ãû£¬²¢ÇÒÎÞ·µ»ØÀàÐÍ¡£
//- (void)testExample //{ // XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); //} - (void)testTrue { XCTAssert(1, @"Can not be zero"); } |
Command + U£¬¸ã¶¨£º

×¢Òâ×ó±ßµÄTest Navigator£¬ÂÌÉ«µÄ±êÖ¾±íʾ²âÊÔÈ«²¿Í¨¹ý¡£
2.²âÊÔµÄ˳Ðò
Èç¹ûÔÚͬһ²âÊÔÀàÎļþÖжàд¼¸¸ö·½·¨£¬ÀýÈ磺
- (void)testTrue2 { NSLog(@"2222222222222222222222"); XCTAssert(1, @"Can not be zero"); } - (void)testTrue1 { NSLog(@"1111111111111111111111"); XCTAssert(1, @"Can not be zero"); } - (void)testTrue3 { NSLog(@"3333333333333333333333"); XCTAssert(1, @"Can not be zero"); } - (void)testAtrue { NSLog(@"0000000000000000000000"); XCTAssert(1, @"Can not be zero"); } |
¿ØÖÆÌ¨²¿·ÖÊä³ö£º
Test Case '-[UnitTestDemoTests testAtrue]' started. 2014-03-19 21:19:38.182 UnitTestDemo[7401:60b] 0000000000000000000000 Test Case '-[UnitTestDemoTests testAtrue]' passed (0.001 seconds). Test Case '-[UnitTestDemoTests testTrue1]' started. 2014-03-19 21:19:38.183 UnitTestDemo[7401:60b] 1111111111111111111111 Test Case '-[UnitTestDemoTests testTrue1]' passed (0.000 seconds). Test Case '-[UnitTestDemoTests testTrue2]' started. 2014-03-19 21:19:38.184 UnitTestDemo[7401:60b] 2222222222222222222222 Test Case '-[UnitTestDemoTests testTrue2]' passed (0.013 seconds). Test Case '-[UnitTestDemoTests testTrue3]' started. 2014-03-19 21:19:38.196 UnitTestDemo[7401:60b] 3333333333333333333333 Test Case '-[UnitTestDemoTests testTrue3]' passed (0.001 seconds). |
¿ÉÒÔ¿´µ½ÎÞÂÛÎÒÃÇÔõÑùµ÷»»test·½·¨µÄÊéд˳Ðò£¬Æä²âÊÔ˳Ðò¶¼ÊDz»±äµÄ¡£
Ŀǰ³õ²½µÄ½áÂÛ£º²âÊÔ·½·¨Ö´ÐеÄ˳ÐòÓë·½·¨ÃûÖÐtestºóÃæµÄ×Ö·û´óСÓйأ¬Ð¡ÕßÓÅÏÈ£¬ÀýÈçtestA£¬testB1£¬testB2Èý¸ö·½·¨Ïà¼ÌÖ´ÐС£
3.¶ÏÑÔ²âÊÔ
ÏÂÃæÒ»¹²18¸ö¶ÏÑÔ£¨SDKÖÐÒ²ÊÇ18¸ö£¬Æäº¬Òåת×Ôios UnitTest
ѧϰ±Ê¼Ç£¬ÕæÐÄÅå·þÔÎĵIJ©Ö÷£¬²¿·ÖºêСµÜÒѾ²âÊÔ¹ý£©£º
XCTFail(format¡) Éú³ÉÒ»¸öʧ°ÜµÄ²âÊÔ£»
XCTAssertNil(a1, format...)Ϊ¿ÕÅжϣ¬a1Ϊ¿Õʱͨ¹ý£¬·´Ö®²»Í¨¹ý£»
XCTAssertNotNil(a1, format¡)²»Îª¿ÕÅжϣ¬a1²»Îª¿Õʱͨ¹ý£¬·´Ö®²»Í¨¹ý£»
XCTAssert(expression, format...)µ±expressionÇóֵΪTRUEʱͨ¹ý£»
XCTAssertTrue(expression, format...)µ±expressionÇóֵΪTRUEʱͨ¹ý£»
XCTAssertFalse(expression, format...)µ±expressionÇóֵΪFalseʱͨ¹ý£»
XCTAssertEqualObjects(a1, a2, format...)ÅжÏÏàµÈ£¬[a1 isEqual:a2]ֵΪTRUEʱͨ¹ý£¬ÆäÖÐÒ»¸ö²»Îª¿Õʱ£¬²»Í¨¹ý£»
XCTAssertNotEqualObjects(a1, a2, format...)Åжϲ»µÈ£¬[a1
isEqual:a2]ֵΪFalseʱͨ¹ý£»
XCTAssertEqual(a1, a2, format...)ÅжÏÏàµÈ£¨µ±a1ºÍa2ÊÇ CÓïÑÔ±êÁ¿¡¢½á¹¹Ìå»òÁªºÏÌåʱʹÓÃ,ʵ¼Ê²âÊÔ·¢ÏÖNSStringÒ²¿ÉÒÔ£©£»
XCTAssertNotEqual(a1, a2, format...)Åжϲ»µÈ£¨µ±a1ºÍa2ÊÇ CÓïÑÔ±êÁ¿¡¢½á¹¹Ìå»òÁªºÏÌåʱʹÓã©£»
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)ÅжÏÏàµÈ£¬£¨double»òfloatÀàÐÍ£©Ìṩһ¸öÎó²î·¶Î§£¬µ±ÔÚÎó²î·¶Î§£¨+/-accuracy£©ÒÔÄÚÏàµÈʱͨ¹ý²âÊÔ£»
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)
Åжϲ»µÈ£¬£¨double»òfloatÀàÐÍ£©Ìṩһ¸öÎó²î·¶Î§£¬µ±ÔÚÎó²î·¶Î§ÒÔÄÚ²»µÈʱͨ¹ý²âÊÔ£»
XCTAssertThrows(expression, format...)Òì³£²âÊÔ£¬µ±expression·¢ÉúÒ쳣ʱͨ¹ý£»·´Ö®²»Í¨¹ý£»£¨ºÜ±ä̬£©
XCTAssertThrowsSpecific(expression, specificException,
format...) Òì³£²âÊÔ£¬µ±expression·¢ÉúspecificExceptionÒ쳣ʱͨ¹ý£»·´Ö®·¢ÉúÆäËûÒì³£»ò²»·¢ÉúÒì³£¾ù²»Í¨¹ý£»
XCTAssertThrowsSpecificNamed(expression, specificException,
exception_name, format...)Òì³£²âÊÔ£¬µ±expression·¢Éú¾ßÌåÒì³£¡¢¾ßÌåÒì³£Ãû³ÆµÄÒ쳣ʱͨ¹ý²âÊÔ£¬·´Ö®²»Í¨¹ý£»
XCTAssertNoThrow(expression, format¡)Òì³£²âÊÔ£¬µ±expressionûÓз¢ÉúÒ쳣ʱͨ¹ý²âÊÔ£»
XCTAssertNoThrowSpecific(expression, specificException,
format...)Òì³£²âÊÔ£¬µ±expressionûÓз¢Éú¾ßÌåÒì³£¡¢¾ßÌåÒì³£Ãû³ÆµÄÒ쳣ʱͨ¹ý²âÊÔ£¬·´Ö®²»Í¨¹ý£»
XCTAssertNoThrowSpecificNamed(expression, specificException,
exception_name, format...)Òì³£²âÊÔ£¬µ±expressionûÓз¢Éú¾ßÌåÒì³£¡¢¾ßÌåÒì³£Ãû³ÆµÄÒ쳣ʱͨ¹ý²âÊÔ£¬·´Ö®²»Í¨¹ý
ÌØ±ð×¢ÒâÏÂXCTAssertEqualObjectsºÍXCTAssertEqual¡£
XCTAssertEqualObjects(a1, a2, format...)µÄÅжÏÌõ¼þÊÇ[a1 isEqual:a2]ÊÇ·ñ·µ»ØÒ»¸öYES¡£
XCTAssertEqual(a1, a2, format...)µÄÅжÏÌõ¼þÊÇa1 == a2ÊÇ·ñ·µ»ØÒ»¸öYES¡£
¶ÔÓÚºóÕߣ¬Èç¹ûa1ºÍa2¶¼ÊÇ»ù±¾Êý¾ÝÀàÐͱäÁ¿£¬ÄÇôֻÓÐa1 == a2²Å»á·µ»ØYES¡£ÀýÈçÏÂÃæ´úÂëÖÐÖ»ÓеڶþÐпÉÒÔͨ¹ý²âÊÔ£º
// 1.±È½Ï»ù±¾Êý¾ÝÀàÐͱäÁ¿ XCTAssertEqual(1, 2, @"a1 = a2 shoud be true"); // ÎÞ·¨Í¨¹ý²âÊÔ XCTAssertEqual(1, 1, @"a1 = a2 shoud be true"); // ͨ¹ý²âÊÔ |
µ«ÊÇ£¬Èç¹ûa1ºÍa2¶¼ÊÇÖ¸Õ룬ÄÇôֻÓÐa1ºÍa2Ö¸Ïòͬһ¸ö¶ÔÏó²Å»á·µ»ØYES¡£ÀýÈçÏÂÃæµÄ´úÂëÖУº
// 3.±È½ÏNSArray¶ÔÏó NSArray *array1 = @[@1]; NSArray *array2 = @[@1]; NSArray *array3 = array1; XCTAssertEqual(array1, array2, @"a1 and a2 should point to the same object"); // ÎÞ·¨Í¨¹ý²âÊÔ XCTAssertEqual(array1, array3, @"a1 and a2 should point to the same object"); // ͨ¹ý²âÊÔ |
array1ºÍarray2Ö¸Ïò²»Í¬¶ÔÏó£¬ÎÞ·¨Í¨¹ý²âÊÔ¡£
ÕâÀï±È½ÏÆæ¹ÖµÄÊÇ£¬NSStringÁíµ±±ðÂÛ£º
// 2.±È½ÏNSString¶ÔÏó NSString *str1 = @"1"; NSString *str2 = @"1"; NSString *str3 = str1; XCTAssertEqual(str1, str2, @"a1 and a2 should point to the same object"); // ͨ¹ý²âÊÔ XCTAssertEqual(str1, str3, @"a1 and a2 should point to the same object"); // ͨ¹ý²âÊÔ |
¾¡¹Üstr1ºÍstr2Ö¸Ïò²»Í¬µÄ¶ÔÏ󣬵«ÊǶþÕßµÄÖ¸Õë±È½ÏÈ´ÄÜͨ¹ý²âÊÔ¡£²»ÖªµÀÕâÊDz»ÊÇXCTest¿ò¼Ü±¾ÉíµÄÒ»¸öBug£¬·´ÕýÔÚÕâÀïʹÓÃNSStringҪСÐľÍÊÇÁË¡£
ÓÉÓÚstr1ºÍstr2Ö¸Ïòͬһ³£Á¿£¬³£Á¿ÔÚÄÚ´æµÄdata¶ÎÖеØÖ·Êǹ̶¨µÄ£¬ËùÒÔ¶þÕßµØÖ·Ïàͬ¡£
ÕÆÎÕÁ˸÷¸ö¶ÏÑԵĺ¬Ò壬ÓÃÆðÀ´¾Íûʲô´óÎÊÌâÁË¡£
4.¼òµ¥µÄÓ¦ÓÃ
˵ÁËÒ»´ó¶ÑÀíÂۺͶ¨Ò壬ÏÂÃæÀ´µãʵ¼ÊµÄÓ¦Óá£ÏÂÃæÓÐÒ»¸ö±í¸ñ¿ØÖÆÆ÷£º
#import "TableViewController.h" #import "TableDataSource.h" static NSString * const kCellIdentifier = @"Cell"; @interface TableViewController () @property (strong, nonatomic) TableDataSource *dataSource; @end @implementation TableViewController @synthesize dataSource = _dataSource; - (void)viewDidLoad { [super viewDidLoad]; TableViewCellConfigureBlock cellConfigureBlock = ^(UITableViewCell *cell, NSString *item) { cell.textLabel.text = item; }; NSArray *stringsArray = @[@"1", @"2", @"3", @"1", @"2", @"3", @"1", @"2", @"3",
@"1", @"2", @"3", @"1", @"2", @"3", @"1", @"2", @"3", @"1", @"2", @"3", @"1", @"2", @"3"]; self.dataSource = [[TableDataSource alloc] initWithItems:stringsArray CellIdentifier:kCellIdentifier ConfigureCellBlock:cellConfigureBlock]; self.tableView.dataSource = _dataSource; } @end |
¸ÃÀàµÄUITableViewDataSourceίÍÐÓÉÒ»¸öTableDataSourceÀàʵÏÖ£¨½«UITableViewDataSource·ÖÀ룬¼ûObjc.io
#1Lighter View Controllers£©¡£TableDataSourceÀàµÄ³õʼ»¯·½·¨ÈçÏ£º
- (instancetype)initWithItems:(NSArray *)anItems CellIdentifier:(NSString *)aCellIdentifier ConfigureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock { self = [super init]; if (self) { self.items = anItems; self.cellIdentifier = aCellIdentifier; self.configureCellBlock = [aConfigureCellBlock copy]; } return self; } |
ÏÂÃæÐ´Ò»¸öTestsÀà²âÊÔÒ»ÏÂDataSourceµÄ³õʼ»¯·½·¨¡£Ê×ÏÈн¨Ò»¸ötest
case classÀࣺ

¼Ì³Ð×ÔXCTestCaseÀࣺ

ΪÁ˹淶£¬ÎÒÃÇн¨µÄ²âÊÔÀà¶¼Ó¦¸ÃÒÔTests½á⣬ÀýÈçCellConfigureTests¡£
È»ºóд¸ötestDataSourceInitializing·½·¨£º
- (void)testDataSourceInitializing { TableViewCellConfigureBlock cellConfigureBlock = ^(UITableViewCell *cell, NSString *item) { cell.textLabel.text = item; }; TableDataSource *tableSource = [[TableDataSource alloc] initWithItems:@[@"1", @"2", @"3"] CellIdentifier:@"TestCell" ConfigureCellBlock:cellConfigureBlock]; XCTAssertNotNil(tableSource, @"TableView data source should not be nil"); } |
Command + UÔËÐвâÊÔ¡£Èç¹ûTableDataSource³õʼ»¯³É¹¦£¬ÄÇôtableSource½«²»»áΪnil£¬²âÊÔ¾ÍÄÜͨ¹ý¡£
±¾ÎÄÏÈ˵µ½ÕâÀÏÂһƪ²©¿Í˵ÏÂÈçºÎ½èÖú¸ü¼ÓÇ¿´óµÄOCMockºÍGHUnit½øÐе¥Ôª²âÊÔ¡£
iOS²âÊÔ-OCMockºÍGHUnit£¨¶þ£©
|