iOS - 单元测试

2016-08-26  本文已影响32人  sunmumu1222

简单使用单元测试

对方法引用AFN框架的单元测试

不写单元测试的程序员是不合格的,为了让自己成为一名合格的程序员,学习如何写单元测试是很有必要的,这里以Xcode集成的测试框架XCTest为例。

XCTest基础用法

默认的测试类继承自XCTestCase,当然也可以自定义测试类,添加一些公共的辅助方法,所有的测试方法都必须以test开头,且不能有参数,不然不会识别为测试方法。

@interface__Tests:XCTestCase@end@implementation__Tests// 在每一个测试用例开始前调用,用来初始化相关数据- (void)setUp {    [supersetUp];// 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.[supertearDown];}// 测试方法- (void)testExample {// This is an example of a functional test case.// Use XCTAssert and related functions to verify your tests produce the correct results.}// 性能测试方法,通过测试block中方法执行的时间,比对设定的标准值和偏差觉得是否可以通过测试- (void)testPerformanceExample {// This is an example of a performance test case.[selfmeasureBlock:^{// Put the code you want to measure the time of here.}];}/// 我自定义的 针对 Person 类的测试方法- (void)testPerson{}@end

断言

XCTest的断言具体可查阅XCTestAssertions.h文件,这里还是做个简单的总结

//通用断言XCTAssert(expression,format...)//常用断言:XCTAssertTrue(expression,format...)XCTAssertFalse(expression,format...)XCTAssertEqual(expression1, expression2,format...)XCTAssertNotEqual(expression1, expression2,format...)XCTAssertEqualWithAccuracy(expression1, expression2, accuracy,format...)XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy,format...)XCTAssertNil(expression,format...)XCTAssertNotNil(expression,format...)

举个例子

- (void)testExample {//设置变量和设置预期值NSUIntegera =10;NSUIntegerb =15;NSUIntegerexpected =24;//执行方法得到实际值NSUIntegeractual = [selfadd:a b:b];//断言判定实际值和预期是否符合XCTAssertEqual(expected, actual,@"add方法错误!");}-(NSUInteger)add:(NSUInteger)a b:(NSUInteger)b{returna+b;}

从这也能看出一个测试用例比较规范的写法,1:定义变量和预期,2:执行方法得到实际值,3:断言

当然在有些特殊情况下直接使用这些断言,会让代码看起来很臃肿,使用时可以对其进行一定的封装一下:

#define ml_ssertTrue(expr)XCTAssertTrue((expr), @"设置指定的字符串,同下")#define ml_assertFalse(expr)XCTAssertFalse((expr), @"")#define ml_assertNil(a1)XCTAssertNil((a1), @"")#define ml_assertNotNil(a1)XCTAssertNotNil((a1), @"")#define ml_assertEqual(a1, a2)XCTAssertEqual((a1),(a2), @"")#define ml_assertEqualObjects(a1, a2)XCTAssertEqualObjects((a1),(a2), @"")#define ml_assertNotEqual(a1, a2)XCTAssertNotEqual((a1),(a2), @"")#define ml_assertNotEqualObjects(a1, a2)XCTAssertNotEqualObjects((a1),(a2), @"")#define ml_assertAccuracy(a1, a2, acc)XCTAssertEqualWithAccuracy((a1),(a2),(acc))

简单实用准备工作

795875-a207ce29f12fa7f2.png.jpeg

如果是之前创建的项目,里面没有勾选,可以自己创建:

795875-9e3ebd787a699757.png.jpeg

进入到这个类,

setUp是每个测试方法调用执行,

tearDown是每个测试方法调用执行。

testExample是测试方法,和我们新建的没有差别。

不过测试方法必须testXXX的格式,且不能有参数,不然不会识别为测试方法。测试方法的执行顺序是字典序排序。

按快捷键Command + U进行单元测试,这个快捷键是全部测试。

795875-362afa43ea505bb6.png.jpeg

一、简单使用单元测试

1、创建一个Person类,里面有一个test1类方法

2、顺便勾选右边的测试单元按钮,为了对这个.m文件的数据测试

795875-192f3a273594f9e7.png.jpeg

3、如果项目比较小,就可以直接在项目刚创建的tests方法里测试,如果项目比较大的话,就创建一个单独的tests,例如:PersonTets:

795875-89721ddcf3d48789.png.jpeg

4、创建一个测试方法:testPerson:

795875-cbeeb6ba7a19c6ef.png.jpeg

上面也说了:测试方法必须testXXX的格式,且不能有参数,不然不会识别为测试方法

如果前面不出现小菱形,可以Build一下

5、点击小菱形图标,出现绿色说明成功:

795875-fdefc42918aaccb4.png.jpeg

6、有时候需要验证值的话,就使用 XCTAssert(<#expression, ...#>),或者其他的断言方法测试结果;

二、对方法引用AFN框架的单元测试

1、测试带有网络请求的方法,例如:

795875-12be468144d6e31a.png.jpeg

2、 按照之前的那样 [Person test2]的话,点击小菱角会出现找到AFN框架:

795875-d4d646284ee3dd06.png.jpeg

3、需要做一些配置

3.1、复制Target(App) - Build Setting - Header Search Paths 的路径。

795875-27159a355bc9a14f.png.jpeg

3.2、粘贴到Target(UnitTests) - Build Setting - Header - Search Paths里。

3.3、复制Target(App) - Build Setting - User-Defined - PODS_ROOT整条。

3.4、到Target(UnitTests) - Build Setting - User-Defined新建一条PODS_ROOT。

795875-601bbddba0354ed1.png.jpeg

4、OK了!

三、期望

期望实际上是异步测试,当测试异步方法时,因为结果并不是立刻获得,所以我们可以设置一个期望,期望是有时间限定的的,fulfill表示满足期望。

例如

- (void)testAsynExample {// 1. 创建期望XCTestExpectation *exp = [selfexpectationWithDescription:@"这里可以是操作出错的原因描述。。。"];NSOperationQueue*queue = [[NSOperationQueuealloc]init];    [queue addOperationWithBlock:^{//模拟:这个异步操作需要2秒后才能获取结果,比如一个异步网络请求sleep(2);//模拟:获取的异步操作后,获取结果,判断异步方法的结果是否正确XCTAssertEqual(@"a",@"a");//2. 如果断言没问题,就调用fulfill宣布测试满足[exp fulfill];    }];//3. 设置延迟多少秒后,如果没有满足测试条件就报错[selfwaitForExpectationsWithTimeout:3handler:^(NSError* _Nullable error) {if(error) {NSLog(@"Timeout Error: %@", error);        }    }];}

这个测试肯定是通过的,因为设置延迟为3秒,而异步操作2秒就除了一个正确的结果,并宣布了条件满足 [exp fulfill],但是当我们把延迟改成1秒,这个测试用例就不会成功,错误原因是 expectationWithDescription:@"这里可以是操作出错的原因描述。。。

异步测试除了使用expectationWithDescription

以外,还可以使用expectationForPredicate和expectationForNotification

,

掌握expectationWithDescription

即可,后两者者仅仅了解

下面这个例子使用expectationForPredicate 测试方法,代码来自于AFNetworking,用于测试backgroundImageForState方法

- (void)testThatBackgroundImageChanges {    XCTAssertNil([self.buttonbackgroundImageForState:UIControlStateNormal]);NSPredicate*predicate = [NSPredicatepredicateWithBlock:^BOOL(UIButton* _Nonnull button,NSDictionary * _Nullable bindings) {return[button backgroundImageForState:UIControlStateNormal] !=nil;    }];    [selfexpectationForPredicate:predicate              evaluatedWithObject:self.buttonhandler:nil];    [selfwaitForExpectationsWithTimeout:20handler:nil];}

利用谓词计算,button是否正确的获得了backgroundImage,如果正确20秒内正确获得则通过测试,否则失败。

expectationForNotification 方法 ,该方法监听一个通知,如果在规定时间内正确收到通知则测试通过。

- (void)testAsynExample1 {    [selfexpectationForNotification:(@"监听通知的名称xxx") object:nilhandler:nil];    [[NSNotificationCenterdefaultCenter]postNotificationName:@"监听通知的名称xxx"object:nil];//设置延迟多少秒后,如果没有满足测试条件就报错[selfwaitForExpectationsWithTimeout:3handler:nil];}

这个例子也可以用expectationWithDescription实现,只是多些很多代码而已,但是这个可以帮助你更好的理解expectationForNotification 方法和 expectationWithDescription 的区别。同理,expectationForPredicate方法也可以使用expectationWithDescription实现。

functestAsynExample1(){letexpectation = expectationWithDescription("监听通知的名称xxx")letsub =NSNotificationCenter.defaultCenter().addObserverForName("监听通知的名称xxx", object:nil, queue:nil) { (not) ->Voidinexpectation.fulfill()    }NSNotificationCenter.defaultCenter().postNotificationName("监听通知的名称xxx", object:nil)    waitForExpectationsWithTimeout(1, handler:nil)NSNotificationCenter.defaultCenter().removeObserver(sub)}

四、期望实战:

-(void)testRequest{# 创建期望XCTestExpectation *expectation =[selfexpectationWithDescription:@"没有满足期望"];    AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];    sessionManager.responseSerializer= [AFHTTPResponseSerializer serializer];    [sessionManager GET:@"http://www.weather.com.cn/adat/sk/101110101.html"parameters:nilsuccess:^(NSURLSessionDataTask* _Nonnull task,id_Nullable responseObject) {NSLog(@"responseObject:%@", [NSJSONSerializationJSONObjectWithData:responseObject options:1error:nil]);# 不为nil 通过,XCTAssertNotNil(responseObject,@"返回出错");# 满意[expectation fulfill];    } failure:^(NSURLSessionDataTask* _Nullable task,NSError* _Nonnull error) {# 为nil 通过,XCTAssertNil(error,@"请求出错");    }];# 设置5秒的超时时间[selfwaitForExpectationsWithTimeout:3.0handler:^(NSError*error) {if(error) {NSLog(@"Timeout Error: %@", error);        }    }];}

上一篇下一篇

猜你喜欢

热点阅读