samurai-native 学习笔记--samurai中的单元测试
来源:互联网 发布:淘宝工作总结范文 编辑:程序博客网 时间:2024/05/21 14:49
囧么说好呢,大神就是任性,自己写了个单元测试类,我们来看看吧
使用
// ----------------------------------// Unit test// ----------------------------------#pragma mark -TEST_CASE( Core, NSDictionary_Extension ){ NSDictionary * _testDict;}DESCRIBE( before ){ _testDict = @{ @"k1": @"v1", @"k2": @"v2", @"k3": @3, @"k4": @{ @"a": @4 } };}DESCRIBE( objectAtPath ){ id value1 = [_testDict objectForOneOfKeys:@[@"k1", @"k2"]]; id value2 = [_testDict objectForOneOfKeys:@[@"k2"]]; EXPECTED( [value1 isEqualToString:@"v1"] ); EXPECTED( [value2 isEqualToString:@"v2"] ); id value3 = [_testDict numberForOneOfKeys:@[@"k3"]]; EXPECTED( [value3 isEqualToNumber:@3] ); id value4 = [_testDict stringForOneOfKeys:@[@"k1"]]; EXPECTED( [value4 isEqualToString:@"v1"] ); id obj1 = [_testDict objectAtPath:@"k4.a"]; EXPECTED( [obj1 isEqualToNumber:@4] ); id obj2 = [_testDict objectAtPath:@"k4.b"]; EXPECTED( nil == obj2 ); obj2 = [_testDict objectAtPath:@"k4.b" otherwise:@"b"]; EXPECTED( obj2 && [obj2 isEqualToString:@"b"] ); id obj3 = [_testDict objectAtPath:@"k4"]; EXPECTED( obj3 && [obj3 isKindOfClass:[NSDictionary class]] );}DESCRIBE( after ){ _testDict = nil;}TEST_CASE_END
实现
// TEST_CASE宏展开后是一个类,前面一个参数是模块名字#define TEST_CASE( __module, __name ) \ @interface __TestCase__##__module##_##__name : SamuraiTestCase \ @end \ @implementation __TestCase__##__module##_##__name// TEST_CASE_END 其实就是 @end#undef TEST_CASE_END#define TEST_CASE_END \ @end
// DESCRIBE 展开后是被测试的方法,方法名字是runTest_xxx#undef DESCRIBE#define DESCRIBE( ... ) \ - (void) macro_concat( runTest_, __LINE__ )
// EXPECTED 展开后如下,如果检测不过,抛出异常#define EXPECTED( ... ) \ if ( !(__VA_ARGS__) ) \ { \ @throw [SamuraiTestFailure expr:#__VA_ARGS__ file:__FILE__ line:__LINE__]; \ }
// REPEAT 和 TIMES 展开后是重复#undef REPEAT#define REPEAT( __n ) \ for ( int __i_##__LINE__ = 0; __i_##__LINE__ < __n; ++__i_##__LINE__ )#undef TIMES#define TIMES( __n ) \ /* [[SamuraiUnitTest sharedInstance] writeLog:@"Loop %d times @ %@(#%d)", __n, [@(__FILE__) lastPathComponent], __LINE__]; */ \ for ( int __i_##__LINE__ = 0; __i_##__LINE__ < __n; ++__i_##__LINE__ )
所有的测试类都是继承自SamuraiTestCase,可是我们看了下SamuraiTestCase的定义结果是一个空的类,定义这个类的作用是为了用runtime找出所有这个类的子类.
通过源码我们可以看出SamuraiUnitTest的实现原理是执行了所有SamuraiTestCase子类的runTest_开头的方法,并且用line排序,实现了特定的初始化方法before和结束方法after,如果验证不过,则抛出一个异常.
下面是SamuraiUnitTest的核心方法
- (void)run{ fprintf( stderr, " =============================================================\n" ); fprintf( stderr, " Unit testing ...\n" ); fprintf( stderr, " -------------------------------------------------------------\n" ); // 获取所有SamuraiTestCase的子类 NSArray * classes = [SamuraiTestCase subClasses]; LogLevel filter = [SamuraiLogger sharedInstance].filter; [SamuraiLogger sharedInstance].filter = LogLevel_Warn;// [SamuraiLogger sharedInstance].filter = LogLevel_All; CFTimeInterval beginTime = CACurrentMediaTime(); for ( NSString * className in classes ) { Class classType = NSClassFromString( className ); if ( nil == classType ) continue; NSString * testCaseName; testCaseName = [classType description]; testCaseName = [testCaseName stringByReplacingOccurrencesOfString:@"__TestCase__" withString:@" TEST_CASE( "]; testCaseName = [testCaseName stringByAppendingString:@" )"]; NSString * formattedName = [testCaseName stringByPaddingToLength:48 withString:@" " startingAtIndex:0];// [[SamuraiLogger sharedInstance] disable]; fprintf( stderr, "%s", [formattedName UTF8String] ); CFTimeInterval time1 = CACurrentMediaTime(); BOOL testCasePassed = YES; // @autoreleasepool { @try { SamuraiTestCase * testCase = [[classType alloc] init]; // 获取所有runTest_开头的方法,runTest_开头的方法看前面的宏定义可以自导后面一个参数是行号 // Samurai在methodsWithPrefix:untilClass:方法里又进行了排序 // 这就是前面的DESCRIBE(before)(after)的实现原理 NSArray * selectorNames = [classType methodsWithPrefix:@"runTest_" untilClass:[SamuraiTestCase class]]; if ( selectorNames && [selectorNames count] ) { for ( NSString * selectorName in selectorNames ) { SEL selector = NSSelectorFromString( selectorName ); // 执行这个方法 if ( selector && [testCase respondsToSelector:selector] ) { NSMethodSignature * signature = [testCase methodSignatureForSelector:selector]; NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget:testCase]; [invocation setSelector:selector]; [invocation invoke]; } } } } @catch ( NSException * e ) { if ( [e isKindOfClass:[SamuraiTestFailure class]] ) { SamuraiTestFailure * failure = (SamuraiTestFailure *)e; [self writeLog: @" \n" " %@ (#%lu) \n" " \n" " { \n" " EXPECTED( %@ ); \n" " ^^^^^^ \n" " Assertion failed\n" " } \n" " \n", failure.file, failure.line, failure.expr]; } else { [self writeLog:@"\nUnknown exception '%@'", e.reason]; [self writeLog:@"%@", e.callStackSymbols]; } testCasePassed = NO; } @finally { } }; CFTimeInterval time2 = CACurrentMediaTime(); // 记录时间 CFTimeInterval time = time2 - time1;// [[SamuraiLogger sharedInstance] enable]; if ( testCasePassed ) { _succeedCount += 1; fprintf( stderr, "[ OK ] %.003fs\n", time ); } else { _failedCount += 1; fprintf( stderr, "[FAIL] %.003fs\n", time ); } [self flushLog]; } CFTimeInterval endTime = CACurrentMediaTime(); CFTimeInterval totalTime = endTime - beginTime; float passRate = (_succeedCount * 1.0f) / ((_succeedCount + _failedCount) * 1.0f) * 100.0f; fprintf( stderr, " -------------------------------------------------------------\n" ); fprintf( stderr, " Total %lu cases [%.0f%%] %.003fs\n", (unsigned long)[classes count], passRate, totalTime ); fprintf( stderr, " =============================================================\n" ); fprintf( stderr, "\n" ); [SamuraiLogger sharedInstance].filter = filter;}
1 0
- samurai-native 学习笔记--samurai中的单元测试
- samurai-native 学习笔记--samurai中的宏魔法(待更新)
- samurai-native 学习笔记--samurai的启动过程(待完善)
- samurai sudoku
- Samurai's Stroke
- Samurai框架分析--未完待续
- The Last Samurai 最后的武士**
- The Last Samurai, spirit always there!
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第五话 使用Samurai创建游戏
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第零话 前言
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- 侍魂2 SAMURAI SHODOWN&…
- date0411/DemoB.java
- [Android]ListView性能优化之视图缓存
- XMPP协议工作流程讲解
- date0411/DemoC.java
- 【GPS】批量将d文件转换为o文件
- samurai-native 学习笔记--samurai中的单元测试
- 机制和策略
- date0411/DemoD.java
- date0411/DemoE.java
- date0411/jar包.java
- zookeeper使用(二)
- date0411/PackageDemo.java
- oracle利用imp和exp导入导出数据
- date0412/ThreadDemo.java