高期望实现异步单元测试
文章目录
【注意】最后更新于 February 20, 2017,文中内容可能已过时,请谨慎使用。
异步单元测试
在Xcode 6之前的版本里面并没有内置XCTest
,只能是在主线程的RunLoop
里面使用一个while循环
,然后一直等待响应或者直到timeout
.
在Xcode 6里,苹果以XCTestExpection类
的方式向XCTest框架
里添加了测试期望
(test expection)。
XCTest框架中相关的方法:设置期望,实现期望,通知期望,谓词计算期望等,以及期望的传递。
常规实现方式
在主线程里,使用while循环
每隔10毫秒会执行一次,直到有响应或者5秒之后超出响应时间限制才会跳出:
{% codeblock lang:swift %} func testAsyncTheOldWay() { let timeoutDate = Date.init(timeIntervalSinceNow: 5.0) var responseHasArrived = false Alamofire.request(“https://www.baidu.com”).responseData{response in print(“获取到的数据长度:(String(data: response.data!, encoding:String.Encoding.utf8)!)”) responseHasArrived = true XCTAssert((response.data?.count)! > 0) }
while (responseHasArrived == false
&& (timeoutDate.timeIntervalSinceNow > 0))
{
CFRunLoopRunInMode(CFRunLoopMode.defaultMode, 0.01, false)
}
if (responseHasArrived == false)
{
XCTFail("Test timed out")
}
} {% endcodeblock %}
XCTest相关API
在Xcode 6里,苹果以XCTestExpection类
的方式向XCTest框架
里添加了测试期望
(test expection)。
XCTestExpection:高期望(High Expectations)的实现和使用
设置期望,实现期望,通知期望,谓词计算期望等,以及期望的传递。
expectation(description:)
:为XCTest测试单元设置期望
为XCTest单元测试,设置一个测试期望以及错误信息描述,并在某一时刻fulfill
实现该期望对象
{% codeblock lang:swift %}
//expectation(description: String) -> XCTestExpectation
let expection = expectation(description: “失败时显示原因”)
{% endcodeblock %}
fulfill()
:调用它表示测试达到期望值
一般在单元测试通过时调用,告知测试已达期望,这一方法替代了responseHasArrived
作为Flag的方式
{% codeblock lang:swift %}
//- (void)fulfill;
expection.fulfill()
{% endcodeblock %}
waitForExpectations(timeout:handler:)
:在方法底部设置测试期望的时效
在方法底部指定一个超时,如果测试条件不适合时间范围便会结束执行
{% codeblock lang:swift %}
// open func waitForExpectations(timeout: TimeInterval, handler: XCTest.XCWaitCompletionHandler? = nil)
waitForExpectations(timeout: 5) { error in
print(“错误信息:(error?.localizedDescription)”)
}
{% endcodeblock %}
如果完成处理的代码在指定时限里执行并调用了fulfill()
方法,那么就说明所有的测试期望在此期间都已经被实现。否则就测试就被打断不再执行
expectation(forNotification:object:handler:):通知期望
该方法监听一个通知,如果在规定时间内正确收到通知则测试通过 {% codeblock lang:swift %} //expectation(forNotification notificationName: String, object objectToObserve: Any?, handler: XCTest.XCNotificationExpectationHandler? = nil) -> XCTestExpectation //设置一个测试通知期望 expectation(forNotification: “BLDownloadImageNotification”, object: nil) {(notification) -> Bool in let userInfo = notification.userInfo as! [String:String] let name = userInfo[“name”] print(“name:(name)”) return true } 来定义一个通知并发送通知,来测试: let notif = Notification.Name(rawValue: “BLDownloadImageNotification”) NotificationCenter.default.post(name: notif, object: self, userInfo: [“name”:“iTBoyer”,“sex”:“man”])
//设置延迟多少秒后,如果没有满足测试条件就报错 waitForExpectations(timeout: 3, handler: nil) {% endcodeblock %}
使用expectation(description:)
实现
帮助理解 expectation(forNotification:object:handler:)
方法和 expectation(description:)
的区别
{% codeblock lang:swift %}
func testAsynForNotificationWithExpectation()
{
let expectation = self.expectation(description: “BLDownloadImageNotification”)
let notif = NSNotification.Name(rawValue: “BLDownloadImageNotification”)
let sub = NotificationCenter.default.addObserver(forName: notif, object: nil, queue: nil) { (notification) -> Void in
expectation.fulfill()
}
//发送一个通知
NotificationCenter.default.post(name: notif, object: nil)
//waitForExpectations
waitForExpectations(timeout: 1, handler: nil)
//移除通知
NotificationCenter.default.removeObserver(sub)
}
{% endcodeblock %}
expectation(for:evaluatedWith:handler:):谓词计算测试法
利用谓词计算,判断button
的backgroundImageForState
方法,是否正确的获得了backgroundImage
,如果20秒
内正确获得则通过测试,否则失败
{% codeblock lang:swift %}
//open func expectation(for predicate: NSPredicate, evaluatedWith object: Any, handler: XCTest.XCPredicateExpectationHandler? = nil) -> XCTestExpectation
func testThatBackgroundImageChanges()
{
let viewController = OnclickLikeViewController()
//viewController.loadView() //不执行viewDidload方法
let _ = viewController.view
let button = viewController.button
let img = button.backgroundImage(for: .normal)
XCTAssertNil(img,“此时img不为nil,中止执行”) //当img不是nil时,执行断言
let predicate = NSPredicate.init { (anyobject, bindings) -> Bool in
//
let button = anyobject as! UIButton
return button.backgroundImage(for: UIControlState()) != nil
}
expectation(for: predicate, evaluatedWith: button, handler: nil)
waitForExpectations(timeout: 20, handler: nil)
}
{% endcodeblock %}
使用expectation(description:)
实现
帮助理解 expectation(for:evaluatedWith:handler:)
方法和 expectation(description:)
的区别
{% codeblock lang:swift %}
func testThatBackgroundImageChanges()
{
//设置期望
let expectation = self.expectation(description: “backgroundImageForState”)
let viewController = OnclickLikeViewController()
//viewController.loadView() //不执行viewDidload方法
let _ = viewController.view
let button = viewController.button
let img = button.backgroundImage(for: .normal)
XCTAssertNil(img,"此时img不为nil,中止执行") //当img不是nil时,执行断言
let predicate = NSPredicate.init { (anyobject, bindings) -> Bool in
//
let button = anyobject as! UIButton
return button.backgroundImage(for: UIControlState()) != nil
//实现测试期望
expectation.fulfill()
}
//等待期望实现
waitForExpectations(timeout: 20, handler: nil)
}
{% endcodeblock %}
传递expectation在目的方法中再fulfill()实现期望
例如将期望封装在字典中,通过通知来传递给异步下载的方法中调用该期望的fulfill()
方法,实现单元测试的期望
{% codeblock lang:swift %}
*/
func testAsynForNotificationWithExpectation2()
{
let expectation = self.expectation(description: “BLDownloadImageNotification”)
let notif = Notification.Name(rawValue: "BLDownloadImageNotification")
NotificationCenter.default.addObserver(self, selector: #selector(AsyncTheOldWayTest.downLoadImage(_:)), name: notif, object: nil)
//将期望封装在字典中传递
let userInf = ["name":"iTBoyer","sex":"man","expectation":expectation]
NotificationCenter.default.post(name: notif, object: self, userInfo: userInf)
//等待期望实现
waitForExpectations(timeout: 1, handler: nil)
NotificationCenter.default.removeObserver(self)
}
//
func downLoadImage(_ notification:Notification)
{
//
let userInfo = notification.userInfo as! [String:AnyObject]
let name = userInfo["name"]
let sex = userInfo["sex"]
print("name:\(name), sex = \(sex)")
let expectation = userInfo["expectation"] as! XCTestExpectation
expectation.fulfill()
}
} {% endcodeblock %}
文章作者 iTBoyer
上次更新 2017-02-20