How to write Unit test cases in MVVM design Swift?

Hi All,

  1. 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.
  2. 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 {
 
    }
  }
}
How to write Unit test cases in MVVM design Swift?
 
 
Q