Xcode 6异步测试<转写>

苹果在2013年推出了一款叫做XCTest的Xcode测试框架,实在是喜闻乐见。由于旧有的测试框架更新停滞数年,一些第三方测试工具和测试框架争相提供了许多新功能和新特性。这次XCode中内置XCTest的决策让开发者们重拾旧爱,并且苹果今年还在Xcode 6中添加了此前漏掉的几项功能,这当中的异步测试功能更是让我欣喜异常。

如果我们的测试项目要执行一个异步任务,它可能会跑在其它的线程里,也可能会跑在主线程的RunLoop里,在这种时候我们应该如何去进行测试呢?

假如现在有一个web请求的功能需要测试。我们会开始web请求然后进入阻塞,然后随便在程序完成的代码块中做一个测试断言。然而,鉴于没有web请求的情况下更谈不上响应,为了调用程序完成部分的代码我们就需要在断言之前确保测试方法已经在执行。

下面来看一个下载web页面类的测试。在通常情况下我们不会真的去在测试时候去做web响应,而是用一些工具来中断响应(比如我偏爱的OHHTTPStubs)。不过在下面的例子里面我们需要更改一下规则——真正的完成一个web响应来进行测试。

我们使用了一个URL和一个程序完成块来对这个类进行测试,它会下载URL所指的页面并调用这个程序块,如果成功会得到一个包含web页面的字串,而发生错误的时候则是一个空字符串。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

- (void)testCodeYouShouldNeverWrite

    {

        __block NSString *pageContents = nil;

        

        [self.pageLoader requestUrl:@"http://bignerdranch.com"

                  completionHandler:^(NSString *page) {

                  

            NSLog(@"The web page is %ld bytes long.", page.length);

            pageContents = page;

            // Test method ends before this test assertion is called

            XCTAssert(pageContents.length > 0); 

        }];

        

        // Nothing prevents the test method from returning before 

        // completionHandler is called.

    }

在Xcode 6之前的版本里面并没有内置XCTest,想使用Xcode测试的只能是在主线程的RunLoop里面使用一个while循环,然后一直等待响应或者直到timeout,下面就是这种老旧方法的代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

- (void)testAsyncTheOldWay

    {

        NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:5.0];

        __block BOOL responseHasArrived = NO;

    

        [self.pageLoader requestUrl:@"http://bignerdranch.com"

                  completionHandler:^(NSString *page) {

                  

            NSLog(@"The web page is %ld bytes long.", page.length);

            responseHasArrived = YES;

            XCTAssert(page.length > 0);

        }];

    

        while (responseHasArrived == NO && ([timeoutDate timeIntervalSinceNow] > 0)) {

            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, YES);

        }

    

        if (responseHasArrived == NO) {

            XCTFail(@"Test timed out");

        }

    }

这个while循环在主线程里面每隔10毫秒会跑一次,直到有响应或者5秒之后超出响应时间限制才会跳出。这个方法十分有效而且看上去也不糟,但这并不意味着开发的终结——这个方法还是不够好。
接下来说一个更加优化的办法。
高期望(High Expectations)

在Xcode 6里,苹果以XCTestExpection类的方式向XCTest框架里添加了测试期望(test expection)。当我们实例化一个测试期望(XCTestExpectation)的时候,测试框架就会预计它在之后的某一时刻被实现。最终的程序完成代码块中的测试代码会调用XCTestExpection类中的fulfill方法来实现期望。这一方法替代了我们之前例子里面使用responseHasArrived作为Flag的方式,这时我们让测试框架等待(有时限)测试期望通过XCTestCase的waitForExpectationsWithTimeout:handler:方法实现。如果完成处理的代码在指定时限里执行并调用了fulfill方法,那么就说明所有的测试期望在此期间都已经被实现。否则,这个测试就悲剧了,它会默默的存在程序中而不会被实现哪怕一次……

当然,失败结果并不意味着失败的测试,只有不明就里的测试结果才算失败的测试。
下面是使用XCTestExpection的示例:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

(void)testWebPageDownload

    {

        XCTestExpectation *expectation =

            [self expectationWithDescription:@"High Expectations"];

        [self.pageLoader requestUrl:@"http://bignerdranch.com"

                  completionHandler:^(NSString *page) {

                  

            NSLog(@"The web page is %ld bytes long.", page.length);

            XCTAssert(page.length > 0);

            [expectation fulfill];

        }];

    

        [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {

            if (error) {

                NSLog(@"Timeout Error: %@", error);

            }

        }];

    }

在测试期望里面输出一些信息以便增强测试结果的可读性。在最后的代码段里面使用[expectation fulfill]来告知此次测试所期望的部分已经确切实现过了。然后用waitForExpectationsWithTimeout:handler方法等待响应,这段会在接受响应之后执行……或者超时之后也会执行。
在OC中实现效果不错,此外我们在Swift下同样也可以实现这个测试:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

func testWebPageDownload() {

        

        let expectation = expectationWithDescription("Swift Expectations")

        

        self.pageLoader.requestUrl("http://bignerdranch.com", completion: {

            (page: String?) -> () in

            if let downloadedPage = page {

                XCTAssert(!downloadedPage.isEmpty, "The page is empty")

                expectation.fulfill()

            }

        })

        

        waitForExpectationsWithTimeout(5.0, handler:nil)

    }

(本文由CocoaChina翻译,转载请注明出处。)

时间: 2024-10-14 00:47:42

Xcode 6异步测试<转写>的相关文章

Xcode真机测试could not find developer disk image解决方法

在使用Xcode进行真机调试的时候,有时根据真机的系统不同,会出现could not find developer disk image 错误,这是由于真机系统过高或者过低,Xcode中没有匹配的配置包文件,我们可以通过这个路径进入配置包的存放目录: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 里面有类似这样的一些文件夹,如果这些文件夹中没有包含我们真机的系统,则不能进行

使用karma测试平时写的小demo(arguments为例)

有人说前端自动化测试非常困难,我觉得确实如此.在项目中,我个人也不放心写的测试,还是要手动测试.但是我们平时写demo学习时,完全可以使用自动化测试. 传统demo 1,新建一个html 2,写入js脚本 3,运行html 平时写demo,大家伙恐怕都是这个步骤吧,其实我们可以使用karma自动化这个过程. 自动化demo(使用karma) 假设已经安装好karma,如果不会,请看本人的这篇博客 karma单元测试入门 1,在根目录运行 karma init 一路空格选择默认,在What is

【转】Xcode真机测试could not find developer disk image解决方法

在使用Xcode进行真机调试的时候,有时根据真机的系统不同,会出现could not find developer disk image 错误,这是由于真机系统过高或者过低,Xcode中没有匹配的配置包文件,我们可以通过这个路径进入配置包的存放目录: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 里面有类似这样的一些文件夹,如果这些文件夹中没有包含我们真机的系统,则不能进行

Xcode进行异步任务的单元测试

在 Xcode 6.0 之前实现异步功能的单元测试很困难 现在通过expectation可以很容易实现异步功能的单元测试 // 1. 定义一个"期望" -> 描述异步的需求,只是一个标记而已 let expectation = expectationWithDescription("xxxTask") // 2. 在异步任务完成后,通过一下方式标记"期望达成" //expectation.fulfill() // 3. 等待期望达成 //

测试技术培训:如何测试磁盘写的速度 2

5.程序实现如下(非常简单.不再解释): // writeFileDemo.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include <fstream> using namespace std; #include <windows.h> #include <WinBase.h> #include <ctime> //目标写入文件 const

Xcode无证书测试

在Xcode7发布以后,即便我们没有开发者账号,我们也可以进行真机测试了. 方式是非常简单的,几个步骤就搞定了....1.打开Xcode 7,按cmd + ,进入偏好设置,Preferences-->Accounts,点击左下角的+号按钮,添加自己的开发者账号,如下:账号不需要是付费的开发者账号,普通的Apple ID即可. 2.选择我们要测试的项目,然后选择General选项卡,修改Team选项为我们刚刚添加的Apple ID 3.Team选项下面如果有警告,点击Fix Issue,等待Xco

Spring MVC 异步测试

从spring3.2开始,支持servlet3的异步请求,这对于处理耗时的请求如缓慢的数据库查询是非常有好处的,不至于很快的耗光servlet的线程池,影响可扩展性. 让我们先来了解一下servlet是怎么处理异步操作的: 通过调用request.startAsync(),ServletRequest就变成异步模式.主要的影响是Servlet.Filter会退出,但是Response保持打开用来完成请求处理. 调用request.startAsync()返回AsyncContext实例,可进一步

解决Xcode真机测试时ineligible devices的问题

升级了Xcode到6.3,连接真机测试时,出现不能选择设备.如图: 设备系统版本是8.3的,Xcode连接其他低系统版本的设备做真机测试时就不会有这个问题. 有人说这是Xcode6.3的bug. 我的解决方法:在Xcode的菜单中Product -> Destination,从这里选择真机. 还不能解决可参考一下方法: 在Build Settings中 iOS Deployment Target 设置不大于真机系统的版本: 重启mac.Xcode,重连设备等: 希望能解决你遇到的相同问题.

CC3200底板测试-烧写CC3200-LAUNCHXL

1. 拿到板子,先研究一下几个跳线帽的作用.我在底板上测到VCC_DCDC_3V3和VCC_BRD之间应该有一个跳线帽的,但是在原理上找不到. 2. LED灯的用途,测试的时候,发现这个灯有时候亮,有时候不亮. 3. 串口驱动,正确的串口驱动是如下,带仿真器 4. 错误的串口驱动,这个应该是FT232外接的SPI flash芯片没有烧写固件, 5. 烧写SPI flash芯片固件 6. 用uniflash烧写CC3200程序,测试下ADC例程 7. ADC例程串口输出,波特率115200 原文地