Mocks in Swift

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 也很简单。

你可能感兴趣的:(Mocks in Swift)