Mock 可以避免我们在写单元测试的过程中不产生脏数据,还有如果你需要测试的方法中包含网络请求的时候,你无法确定网络请求的稳定性,测试次数越多,失败的可能性就越高,就如下面这段网络请求的代码,我们就需要对请求进行Mock。
func requestXingShuLin() {
let session = URLSession()
let url = URL(string: "http://www.xingshulin.com")!
let task = session.dataTask(with: url) { (data, _, _) -> Void in
if let data = data {
let string = String(data: data, encoding: String.Encoding.utf8)
print(string)
}
}
task.resume()
}
在Objective-C 中,我们可以使用OCMock简单的实现Mock,从技术上讲,你也可以在Swift 中也可以使用OCMock,但是有很严格的限制,并且功能有限,因为OCMock是利用反射在运行时改变类的类型,而Swift 大多时候是无法在运行时修改的,虽然你可以使用dynamic 来增加一些动态性,但这是不推荐的,可能在以后Swift 也不会支持,因为Swift 是一个安全的编程语言。Swift目前也有一些可以实现Mock 的框架,如 Dobby、MockFive 、SwiftMock 等。但我更倾向于自己来写Mock。
我们可以使用Swift 中面向协议的思想来完成这次Mock。
我们先定义一个协议,并给URLSession 实现。
protocol URLSessionMockDelegate {
func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> URLSessionDataTask
}
extension URLSession: URLSessionMockDelegate {
}
class RequestTest: NSObject {
let session: URLSessionMockDelegate
let url: URL
init(session: URLSessionMockDelegate, url: URL) {
self.session = session
self.url = url
}
func requestXingShuLin(completeHandler: @escaping (_ success: Bool) -> ()) {
let url = URL(string: "https://www.xingshulin.com")!
let task = session.dataTask(with: url) { (data, _, _) -> Void in
if let data = data {
let string = String(data: data, encoding: String.Encoding.utf8)
print(string)
completeHandler(true)
} else {
completeHandler(false)
}
}
task.resume()
}
}
然后定义一个MockURLSession 实现URLSessionMockDelegate 协议,实现其 dataTask 方法。
func testRequest_Success() {
//Given
class MockURLSession: URLSessionMockDelegate {
func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> URLSessionDataTask {
if url == URL(string: "https://www.xingshulin.com")! {
completionHandler(Data(), nil, nil)
} else {
completionHandler(nil, nil, nil)
}
}
}
let mockSession = MockURLSession()
let url = URL(string: "https://www.xingshulin.com")!
let requestTest = RequestTest(session: mockSession, url: url)
//When
var status = false
requestTest.requestXingShuLin { (isSuccess) in
status = isSuccess
}
//Then
waitForExpectations(timeout: 0.5) { (error) in
XCTAssertTrue(status)
}
}
使用协议来实现Mock 也很简单。