Тестирование HTTP-трафика в приложении Alamofire
Я немного пытаюсь выяснить, как лучше всего тестировать приложение, использующее Alamofire для синхронизации с данными сервера.
Я хочу проверить свой код, который использует Alamofire и обрабатывает ответы JSON с сервера.
Я хотел бы издеваться над этими тестами, чтобы я мог подавать ожидаемые данные ответа на эти тесты без реального сетевого трафика.
Это сообщение в блоге (http://nshipster.com/xctestcase/) описывает, как легко сделать Mock объект в Swift - но я не уверен, как сделайте это с помощью Alamofire и его цепных ответов.
Я высмеиваю менеджера? запрос? Отклик? Любая помощь будет оценена!
Ответы
Ответ 1
Я добавляю еще один ответ, так как я только нашел этот подход, который, на мой взгляд, проще и очень прост для чтения и использования.
Я создал фиктивный класс Alamofire, который содержит только функции и типы, необходимые для тестов.
Теперь я включаю этот файл в тестовую цель вместо реального Alamofire.
Например, я создал свою версию класса Request
, где я определяю пару статических переменных, которые я оцениваю в зависимости от теста, и для этого класса я реализовал только init
и responseJSON
.
public class Request {
var request:String?
struct response{
static var data:NSHTTPURLResponse?
static var json:AnyObject?
static var error:NSError?
}
init (request:String){
self.request = request
}
public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
completionHandler(NSURLRequest(URL: NSURL(string:self.request!)!), Request.response.data, Request.response.json, Request.response.error)
return self
}
}
Теперь я могу высмеять ответ в тесте:
func testMytestFunction(){
var HTMLResponse = NSHTTPURLResponse(URL: NSURL(string: "myurl")!, statusCode: 200, HTTPVersion: "HTTP/1.1", headerFields: nil)
Request.response.data = HTMLResponse
Request.response.json = LoadDataFromJSONFile("MyJsonFile")
request(.POST, "myurl", parameters: nil, encoding: ParameterEncoding.JSON).responseJSON {
(request, response, JSON, error) -> Void in
// the JSON and response variable now contains exactly the data that you have passed to Request.response.data and Request.response.json
}
}
Функция запроса определяется здесь:
public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
return Request(request: URLString.URLString)
}
public func request(URLRequest: URLRequestConvertible) -> Request {
return Request(request: "fakecall")
}
Ответ 2
Этот вопрос стареет, но я столкнулся с одной и той же проблемой, и решение очень просто при использовании OHHTTPStubs.
OHHTTPStubs просто издевается над ответами, полученными от NSURLSession, поэтому он хорошо работает с Alamofire, и вы очень хорошо просматриваете свой код.
Например, в вашем тестовом примере просто высмеивайте ответ, используя:
OHHTTPStubs.stubRequestsPassingTest({
(request: NSURLRequest) -> Bool in
return request.URL!.host == "myhost.com"
}, withStubResponse: {
(request: NSURLRequest) -> OHHTTPStubsResponse in
let obj = ["status": "ok", "data": "something"]
return OHHTTPStubsResponse(JSONObject: obj, statusCode:200, headers:nil)
})
Ответ 3
Ожидание ответа от @mattt Я отправляю пример своего кода.
Скажем, что у нас есть класс Client
, который отвечает за вызов простой веб-службы. Этот класс реализует функцию под названием userSignIn
, которая выполняет знак с использованием WS.
Это код функции userSignIn
:
func userSignIn(
#email:String,
password:String,
completionHandler: (Bool, String?, NSError?) -> Void
)-> Void
{
var parameters:[String:AnyObject] = [
"email":email,
"password":password,
]
Alamofire.request(.POST, Client.urlPath, parameters: parameters, encoding: ParameterEncoding.JSON).responseJSON {
(request, response, JSON, responseError) -> Void in
// Setup callback params
// HERE WE INJECT THE "FAKE" DATA--------
var operationComplete = false
var accessToken:String?
var error:NSError?
// --------------------------------------
if let statusCode = response?.statusCode {
// Check for errors and build response data
(operationComplete, accessToken, error) = self.checkSignInResponse(statusCode, JSON: JSON)
}
// Call the completion handler
completionHandler(operationComplete, accessToken, error)
}
}
Целью функции является получение маркера из веб-службы, если информация, переданная пользователем, верна.
Функция checkSignInResponse
(я не сообщаю свой код, так как она не полезна для ответа) имеет роль для оценки трех переменных operationComplete
, accessToken
и error
в зависимости от полученного ответа JSON.
Теперь, когда 3 переменные имеют значение, мы вызываем completionHandler
, используя их.
Как издеваться над этой функцией?!
Чтобы высмеять ответ, я переопределяю функцию userSignIn
непосредственно в тестовую функцию (как объясняется в статье NSHipster).
func testUserSignIn_whenParamsAreInvalid(){
class MockClient:Client {
override func userSignIn(#email: String, password: String, completionHandler:
(Bool, String?, NSError?) -> Void) {
// Set callback params
var operationComplete = false
var accessToken:String? = nil
var error:NSError? = NSError(domain: "Testing", code: 99, userInfo: nil)
completionHandler(operationComplete, accessToken, error)
}
}
signInViewController!.client = MockClient()
signInViewController!.loadView()
fillRegisterFieldsWithDataAndSubmit(femail(), password: fpassword())
XCTAssertNotNil(signInViewController!.error, "Expect error to be not nil")
}
то я заменяю Client
внутри контроллера представления, который я тестирую, используя мой "издевавшийся" клиент. В этом случае я проверяю, что контроллер передает информацию о функциях недействительно, поэтому я проверяю, что свойство error
контроллера не равно нулю. Чтобы заставить эти данные, я просто установил operationComplete
в false и я вручную создаю NSError
.
Это имеет смысл для вас? Я не уверен, что этот тест - хороший тест... но по крайней мере я могу проверить поток данных.