Hi All,
- I want to learn and write unit test cases using Mock class and stubs for MVVM design implementation. Also I want to test API calls using XCTestCase framweork.
- I need recommended guidelines or way for writing test cases for following class designs.
I am facing problem for how to test code/data from mock class with actual class code. Thanks in Advance for valuable help. Please ignore hardcoded error values.
//NetworkManager.swift
protocol GetAPIRquestData {
var path: String { get set }
var method: String { get set }
func getAPIData(_ completion: @escaping (Result<Data, Error>) -> Void) -> Void
}
class NetworkManager: GetAPIRquestData {
var path: String
var method: String
private let session: URLSession
init(session: URLSession = URLSession(configuration: .default), path: String, method: String) {
self.session = session
self.path = path
self.method = method
}
func getAPIData(_ completion: @escaping (Result<Data, Error>) -> Void) -> Void {
guard let request = try? buildRequest() else {
return
}
let task = session.dataTask(with: request) { data, response, error in
if let data = data {
if let response = response as? HTTPURLResponse, response.statusCode == 200 {
completion(.success(data))
} else {
completion(.failure(NSError(domain: "Invalid data", code: 205, userInfo: nil)))
}
} else {
completion(.failure(NSError(domain: "Data error", code: 400, userInfo: nil)))
}
}
task.resume()
}
func buildRequest() throws -> URLRequest {
// Construct a URL by assigning its parts to a URLComponents value
var components = URLComponents()
components.scheme = "https"
components.host = "api.developer.com"
components.path = self.path
// This will give us the constructed URL as an optional
guard let url = components.url else {
throw NSError(domain: "Invalid URL", code: 10, userInfo: nil)
}
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10)
request.httpMethod = self.method
return request
}
}
//HomeUsersViewModel.swift
struct UsersViewModel {
let loginUserName: String?
let avatarUrl: String?
init(userModel: HomeUsersModelElement) {
self.loginUserName = userModel.login
self.avatarUrl = userModel.avatarURL
}
}
protocol ParsableData {
var arrayUsersViewModel: [UsersViewModel] { get set }
var output: OutputHomeViewData? { get set }
func getParsableData() -> Void
}
protocol OutputHomeViewData: AnyObject {
func didReceivedOutPutData(_ userViewModel: [UsersViewModel]?, error: Error?) -> Void
}
final class HomeUsersViewModel: ParsableData {
weak var output: OutputHomeViewData?
var arrayUsersViewModel: [UsersViewModel] = []
private let getApiData: GetAPIRquestData
init(getApiData: GetAPIRquestData){
self.getApiData = getApiData
}
func getParsableData() -> Void {
getApiData.getAPIData { result in
switch result {
case .success(let data):
do {
let jsonData = try JSONDecoder().decode(HomeUsersModel.self, from: data)
let viewModel = self.mapModelDataToViewModelData(userModel: jsonData)
self.output?.didReceivedOutPutData(viewModel, error: nil)
} catch let error {
self.output?.didReceivedOutPutData(nil, error: error)
}
case .failure(let err):
self.output?.didReceivedOutPutData(nil, error: err)
}
}
}
//Map API response data to View Model and assign to delegete object.
func mapModelDataToViewModelData(userModel: HomeUsersModel) -> [UsersViewModel] {
let modelArray = userModel.map { (obj: HomeUsersModelElement) -> UsersViewModel in
return UsersViewModel(userModel: obj)
}
return modelArray
}
}
//HomeUsersViewController.swift
class HomeUsersViewController: UIViewController {
private var parsableData: ParsableData = HomeUsersViewModel(getApiData: NetworkManager(session: URLSession(configuration: .default), path: "/users", method: "GET"))
private var userVM: [UsersViewModel] = []
override func viewDidLoad() {
super.viewDidLoad()
parsableData.getParsableData()
parsableData.output = self
}
}
extension HomeUsersViewController: OutputHomeViewData {
func didReceivedOutPutData(_ userViewModel: [UsersViewModel]?, error: Error?) {
if let userVM = userViewModel {
self.userVM = userVM
print("VM:\(self.userVM)")
} else if let _ = error {
} else {
}
}
}