簡體   English   中英

如何在Swift中使用OCMock模擬來自AFNetworking的響應?

[英]How to mock response from AFNetworking with OCMock in Swift?

這是我獲取網絡客戶端實例的方式:

let networkClient = DBNetworkClient(baseURL: NSURL(string: "http://mysite.pl/api"))

我也有一種方法:

func citiesWithParameters(parameters: [String: String], completionBlock: DBSearchOptionHandler) {

    GET("cities", parameters: parameters, success: { operation, response in

        if let error = NSError(response: response) {
            completionBlock([], error)
        } else {
            let cities = DBCity.parseCitiesWithDictionary(response as! NSDictionary)
            completionBlock(cities, nil)
        }

        }) { operation, error in

            completionBlock([], error)
    }
}

這就是我所謂的方法:

networkClient.citiesWithParameters(parameters, completionBlock: { cities, error in 
    //do sth 
})

這樣,我傳遞了一些參數,並從服務器獲得了REAL響應。 當我要求時,我想嘲笑那個回應。 這個怎么做?

func testCities() {

    let mockNetworkClient = OCMockObject.mockForClass(DBNetworkClient.classForCoder())        


    //what should I perform here to be able to do sth like this:

    let expectation = expectationWithDescription("")

    mockNetworkClient.citiesWithParameters(["a": "b"]) { cities, error in
        expectation.fulfill()
        XCTAssertNotNil(cities)
        XCTAssertNil(error) //since I know what is the response, because I mocked this
    }

    waitForExpectationsWithTimeout(10, handler: nil)
}

這就是在DBNetworkClient定義我的方法GET方式:

override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {

    return super.GET(URLString, parameters: parameters, success: { (operation, response) in

        print("GET \(operation.request.URL)")
        print("GET \(response)")

        success(operation, response)

        }, failure: { (operation, error) in

            print("GET \(operation.request.URL)")
            print("GET \(operation.responseObject)")

            failure(operation, error)
    })
}

一旦我能夠為我提供50英鎊的賞金 ,將幫助我做到這一點。

不幸的是, 為AFNetworking編寫模擬測試沒有幫助。 它對我不起作用。

我沒有使用Alamofire的經驗,所以我不知道在哪里聲明您的GET方法,但是您絕對應該在此方法中添加citiesWithParameters而不要使用citiesWithParameters

例如,如果在DBNetworkClient聲明了DBNetworkClient

func testCities()
{
    //1
    class DBNetworkClientMocked: DBNetworkClient
    {
        //2
        override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {

            //3
            success(nil, ["San Francisco", "London", "Sofia"])

        }
    }

    //4
    let sut = DBNetworkClientMocked()
    sut.citiesWithParameters(["a":"b"]) { cities, error in

        //5
        XCTAssertNotNil(cities)
        XCTAssertNil(error)

    }
}

那么這里發生了什么:

  1. 您定義的類是DBNetworkClient子類,因此按照您發布的文章中的描述為其創建“模擬”。 在該類中,我們將僅覆蓋我們要更改的方法(存根),並使其他方法保持不變。 這以前是使用OCMock完成的,稱為部分模擬。
  2. 現在,將其存入GET方法以返回特定數據,而不是服務器中的實際數據
  3. 您可以在此處定義返回存根服務器的內容。 可能是成功,失敗等。
  4. 我們願與嘲弄和存根現在,我們創作出如此稱為S ubjectü的nDer 牛逼 EST(SUT)。 請注意,如果DBNetworkClient具有特定的構造函數,則必須調用此構造函數,而不是默認的構造函數-()。
  5. 執行要測試的方法。 在回調內部,我們放入了所有斷言。

到現在為止還挺好。 但是,如果GET方法是Alamofire的一部分,則需要使用一種稱為“依賴注入”的技術(您可以在Google上搜索以獲得更多信息)。

因此,如果GET在另一個類中聲明並且僅在citysWithParameters中引用,我們如何對它進行存根處理? 讓我們看一下:

//1
class DBNetworkClient
{
    //2
    func citiesWithParameters(parameters: [String: String], networkWorker: Alamofire = Alamofire(), completionBlock: DBSearchOptionHandler) {

        //3
        networkWorker.GET("cities", parameters: parameters, success: { operation, response in

            if let error = NSError(response: response) {
                completionBlock([], error)
            } else {
                let cities = DBCity.parseCitiesWithDictionary(response as! NSDictionary)
                completionBlock(cities, nil)
            }

            }) { operation, error in

                completionBlock([], error)
        }

    }
}

func testCities()
{
    //4
    class AlamofireMock: Alamofire
    {
        override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {

            success(nil, ["San Francisco", "London", "Sofia"])

        }
    }

    //5
    let sut = DBNetworkClient()
    sut.citiesWithParameters(["a":"b"], networkWorker: AlamofireMock()) { cities, error in

        //6
        XCTAssertNotNil(cities)
        XCTAssertNil(error)

    }
}
  1. 首先,我們必須稍微更改我們的citiesWithParameters才能接收另一個參數。 此參數是我們的依賴項注入。 是具有GET方法的對象。 在現實生活中的示例中,最好是僅使用協議,因為citiesWithParameters不需要知道的其他信息超過該對象能夠發出請求的能力。
  2. 我已將networkWorker參數設置為默認值,否則您需要citiesWithParameters所有調用citiesWithParameters為新參數要求。
  3. 我們將所有實現保持不變,只是現在我們調用了注入的對象GET方法
  4. 現在回到測試中,我們這次將模擬Alamofire。 我們做了與上一個示例完全相同的操作,只是這次模擬的類是Alamofire而不是DBNetworkClient
  5. 當我們調用citiesWithParameters時,我們將citiesWithParameters對象作為networkWorker傳遞。 這樣,我們的存根GET方法將被調用,我們將從“假”服務器中獲得期望的數據。
  6. 我們的主張保持不變

請注意,這兩個示例不使用OCMock,而是完全依靠Swift的力量! OCMock是一個很棒的工具,我們使用了很棒的動態語言-Objective-C。 但是,關於Swift的動態性和反思性幾乎完全消失了。 這就是為什么即使在OCMock官方頁面上我們也有以下聲明:

會不會有用Swift編寫的Swift模擬框架? 也許。 到目前為止,它看起來不太可能,因為模擬框架在很大程度上取決於對語言運行時的訪問,而Swift似乎沒有提供任何東西。

提供的兩個實現中缺少的是驗證調用GET方法。 我將這樣保留它,因為這不是原始問題,您可以使用在模擬類中聲明的布爾值輕松實現它。

更重要的一點是,我假設Alamofire中的GET方法是實例方法,而不是類方法。 如果不是這樣,則可以在DBNetworkClient內部聲明一個新方法,該方法只需調用Alamofire.GET並在citiesWithParameters內部使用該方法。 然后,您可以像第一個示例一樣對這個方法進行存根,一切都會好起來的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM