单元测试网络调用(失败的测试) - Swift

时间:2015-11-17 00:22:47

标签: ios swift unit-testing

我正在尝试为我的网络电话编写失败的测试,但无论如何都无法从我的测试中访问连接设置。

此代码适用于测试成功案例:

func testRetrieveProducts() {

    let expectation = expectationWithDescription("asynchronous request")

    Requests().retrieveProducts({ (products) -> () in

        // check that we have two arrays returned.
        XCTAssert(products.count == 2)
        expectation.fulfill()

        }) { (error) -> () in
        XCTFail("Request failed")
    }
    waitForExpectationsWithTimeout(5.0, handler: nil)
}

但我一直在寻找一种测试网络超时的方法,并返回不正确的数据。

我可以通过在retrieveProducts函数内单独调用函数并将函数删除来测试不正确的数据,但是做一些像关闭互联网一样简单的事情证明是非常困难的。

我知道我们可以访问网络链接调节器,但是不能选择为每次测试打开和关闭它。

我希望能够访问一些简单的内容:

func testFailRetrieveProducts() {

    let expectation = expectationWithDescription("asynchronous request")
    SomeNetworkClass.disableInternet()

    Requests().retrieveProducts({ (products) -> () in

        }) { (error) -> () in
        XCTAssert(error == SomeError.TimeoutError)
    }
    waitForExpectationsWithTimeout(5.0, handler: nil)
}

那里有任何解决方案可以处理我所追求的事情,或者我是否会解决这个问题?

2 个答案:

答案 0 :(得分:1)

请查看有关Apple的网络链接调节器的NSHipster article。有很多预设,您可以创建自己的自定义网络配置文件。不幸的是,似乎没有办法在代码中限制网络。

然而,一个可行的替代方法是使用ReactiveCocoa并将所有网络事件建模为SignalProducers。然后,您可以使用throttlewait功能,具体取决于您的意图。

答案 1 :(得分:1)

我最后只是嘲笑网络电话,说实话比在实际连接上执行测试要好得多,因为无论如何这些都是非常不可靠的。

这是我的模拟NSURLSession

class MockSession: NSURLSession {

    var completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?

    static var mockResponse: (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)

    override class func sharedSession() -> NSURLSession {
        return MockSession()
    }

    override func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask {
        self.completionHandler = completionHandler
        return MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
    }

    class MockTask: NSURLSessionDataTask {

        typealias Response = (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)
        var mockResponse: Response
        let completionHandler: ((NSData!, NSURLResponse!, NSError!) -> Void)?

        init(response: Response, completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?) {
            self.mockResponse = response
            self.completionHandler = completionHandler
        }

        override func resume() {
            completionHandler!(mockResponse.data, mockResponse.urlResponse, mockResponse.error)
        }

    }
}

以下是我在测试中使用它的方法:

请注意,即使没有网络延迟,您仍然必须使用expectation

func testRetrieveProductsValidResponse() {

    let testBundle = NSBundle(forClass: self.dynamicType)
    let filepath = testBundle.pathForResource("products", ofType: "txt")
    let data = NSData(contentsOfFile: filepath!)
    let urlResponse = NSHTTPURLResponse(URL: NSURL(string: "https://anyURL.com")!, statusCode: 200, HTTPVersion: nil, headerFields: nil)

    MockSession.mockResponse = (data, urlResponse: urlResponse, error: nil)

    let requestsClass = RequestManager()
    requestsClass.Session = MockSession.self

    let expectation = expectationWithDescription("ready")

    requestsClass.retrieveProducts("N/R FOR TEST", branchID: "N/R FOR TEST", products: { (products) -> () in
        XCTAssertTrue(products.count == 7)
        expectation.fulfill()
        }) { (error) -> () in
            XCTAssertFalse(error == Errors.NetworkError, "Its a network error")
            XCTAssertFalse(error == Errors.ParseError, "Its a parse error")
            XCTFail("Error not covered by previous asserts. Shouln't get to here anyway.")

            expectation.fulfill()
    }

    waitForExpectationsWithTimeout(3.0, handler: nil)
}

最后,我的RequestManager类上有一个可访问的属性,我可以在进行测试时将其与MockSession交换。

var Session = NSURLSession.self