TestUtils
包包含以下内容,以使测试更快、更标准化:
NetworkSessionMock
用于设置自定义网络响应和错误。要在您的 Swift 项目中使用此包,请添加以下依赖项:
.package(url: "https://github.com/1904labs/ios-test-utils.git", from 1.0.0)
要在您的 Xcode 项目中使用此包,请选择 File > Swift Package > Add Package Dependency
并输入以下 URL:
https://github.com/1904labs/ios-test-utils.git
此包包含 URLProtocol
模拟,您可以使用它来替换默认的 URLSession
配置。
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [URLProtocolMock.self]
let sessionMock = URLSession(configuration: config)
虽然您可以创建自己的 URLSession
配置,但如果您不需要任何特殊设置,我们已经提供了一个基本示例供您使用。
public var urlSessionMock: URLSession {
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [URLProtocolMock.self]
let session = URLSession(configuration: configuration)
return session
}
作为一个例子,让我们看看如何测试一个调用网络层的 view model。
这是有问题的 view model:
class UserViewModel {
var user: User?
var token: String?
func fetchUser(username: String, password: String, completion: @escaping (Result<User,Error>) -> ()) {
NetworkManager.shared.login(username: username, password: password, completion: { result in
switch result {
case .success(let user, let token):
self.user = user
self.token = token
completion(.success(user))
case .failure(let error):
print(error.localizedDescription)
completion(.failure(error))
}
})
}
}
这是支持类:
struct User: Codable {
let firstName: String
let lastName: String
}
enum NetworkError: Error {
case noToken
case decodingError
}
class NetworkManager {
private init() { }
public static var shared = NetworkManager()
var session = URLSession.shared
func login(username: String, password: String, completion: @escaping (Result<(User, token: String),Error>) -> ()) {
// Make network calls
let loginURL = URL(string: "[Some URL]")!
var request = URLRequest(url: loginURL)
let body = [
"username": username,
"password": password
]
request.httpBody = try! JSONEncoder().encode(body)
self.session.dataTask(with: request, completionHandler: { data, response, error in
let httpResponse = response as? HTTPURLResponse
guard let token = httpResponse?.allHeaderFields["TOKEN"] as? String else {
return completion(.failure(NetworkError.noToken))
}
do {
let user = try JSONDecoder().decode(User.self, from: data!)
return completion(.success((user, token)))
} catch {
return completion(.failure(NetworkError.decodingError))
}
})
}
}
这是我们如何测试该代码:
var viewModel = UserViewModel()
func testExample() throws {
NetworkManager.shared.session = urlSessionMock
let url = URL(string: "[Some URL]")!
let expectedFirstName = "John"
let expectedLastName = "Doe"
let expectedToken = "SomeToken"
let expectedUser = User(firstName: "John", lastName: "Doe")
let data = try! JSONEncoder().encode(expectedUser)
let headerFields: [String:String] = [
"TOKEN": expectedToken
]
URLProtocolMock.testURLs = [
url: [(data, HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: headerFields), nil)]
]
let delayExpectation = expectation(description: "Waiting for task")
viewModel.fetchUser(username: "John", password: "Doe", completion: { result in
delayExpectation.fulfill()
})
waitForExpectations(timeout: 3, handler: nil)
// Perform assertions
XCTAssertEqual(viewModel.user?.firstName, expectedFirstName)
XCTAssertEqual(viewModel.user?.lastName, expectedLastName)
XCTAssertEqual(viewModel.token, expectedToken)
}