首页 > 解决方案 > 如何调整我的视图模型以实现依赖注入 swiftui(用于以后的单元测试)

问题描述

目前我有多个 ViewModel,在阅读了一些其他帖子并获得了帮助之后,我被建议在我的班级中实现依赖注入,以便对我的逻辑进行单元测试。

但是我不确定如何使我的以下类适应依赖注入,并且我不确定是否也需要更改我的 DataManager 类。

这是我的 ViewModel 的一个示例:

 class CalorieProgressViewModel: ObservableObject {


@Published var calorieProgress = [CalorieProgressEntity]()


init() {

}

//Creating the data
func addCalorieProgressData(id: UUID, calorieProgress: Double, fatProgress: Double, carbProgress: Double, proteinPogress: Double, created: Date) {
    CoreDataManager.shared.addCalorieProgressData(id: id, calorieProgress: calorieProgress, fatProgress: fatProgress, proteinProgress: proteinPogress, carbProgress: carbProgress, created: created){ (isAdded, error) in
        if let error = error {
            print(error)
        } else {
            print("Data has been added from addCalorieProgress VM")
        }
    }
}

如您所见,我只是使用 datamanger 类将数据添加到我的核心数据:

import Foundation
import UIKit
import CoreData

class CoreDataManager {
    
    static let shared: CoreDataManager = {
        let appDelegate = AppDelegate.instance!
        let instance = CoreDataManager(managedObjectContext: appDelegate.persistanceContainer.viewContext)
        return instance
    }()
    
    var managedContext: NSManagedObjectContext
    
  private init(managedObjectContext: NSManagedObjectContext) {
        managedContext = managedObjectContext
    }
}

//MARK:- CalorieTracker Insert/Update/Delete
extension CoreDataManager {

func addCalorieProgressData(id: UUID, calorieProgress: Double, fatProgress: Double, proteinProgress: Double, carbProgress: Double, created: Date, completionHandler: @escaping (_ succeed: Bool, _ error: Error?) -> Void) {
        let calorieProgressEntity = NSEntityDescription.insertNewObject(forEntityName: "CalorieProgressEntity", into: managedContext) as? CalorieProgressEntity
        calorieProgressEntity?.id = id
        calorieProgressEntity?.calorieProgress = calorieProgress
        calorieProgressEntity?.fatProgress = fatProgress
        calorieProgressEntity?.proteinProgress = proteinProgress
        calorieProgressEntity?.carbProgress = carbProgress
        calorieProgressEntity?.created = created

       
        
        do {
            try managedContext.save()
            print("context saved for add calorieGoal")
            completionHandler(true, nil)
        } catch let error {
            completionHandler(false, error)
        }
    }
    
    func fetchCalorieProgressData(completionHandler: @escaping (_ succeed: Any?, _ error: Error?) -> Void) {
        var goals = [CalorieProgressEntity]()
        let calorieGoalRequest: NSFetchRequest<CalorieProgressEntity> = NSFetchRequest<CalorieProgressEntity>(entityName: "CalorieProgressEntity")
        
        do {
            goals = try managedContext.fetch(calorieGoalRequest)
            completionHandler(goals, nil)
        } catch let error {
            completionHandler(nil, error)
        }
    }
}

任何帮助将不胜感激 :)

标签: swiftcore-dataswiftui

解决方案


Swift 中有不同的依赖注入方式。您可以通过 来完成init(),然后在创建 ViewModel 时传递您的 CoreDataManager。

class CalorieProgressViewModel: ObservableObject {

    @Published var calorieProgress = [CalorieProgressEntity]()
    let manager: CoreDataManager

    init(manager: CoreDataManager) {
        self.manager = manager
    }

    //Creating the data
    func addCalorieProgressData(id: UUID, calorieProgress: Double, fatProgress: Double, carbProgress: Double, proteinPogress: Double, created: Date) {
        //<< here don't use your singleton, instead use the manager injected via dependency injection
        manager.addCalorieProgressData(id: id, calorieProgress: calorieProgress, fatProgress: fatProgress, proteinProgress: proteinPogress, carbProgress: carbProgress, created: created){ (isAdded, error) in
        if let error = error {

然后,当您创建 ViewModel 时,您会将 CoreDataManager 作为参数传递。在这里你可以使用你的 Singleton。

我建议为 CoreDataManager 创建一个协议。然后您可以轻松地将您的 MockManager 或实际的 CoreDataManager 传递到 ViewModel 以便稍后对其进行测试。

protocol CoreDataManager {
    func addCalorieProgressData(id: UUID, calorieProgress: Double, fatProgress: Double, proteinProgress: Double, carbProgress: Double, created: Date, completionHandler: @escaping (_ succeed: Bool, _ error: Error?) -> Void)
}


class MainCoreDataManager: CoreDataManager {
    ... 

class MockCoreDataManager: CoreDataManager {
    ... mocked here

推荐阅读