Swift MVVM测试策略和代码覆盖率问题

Swift MVVM测试策略和代码覆盖率问题,swift,xcode,unit-testing,mvvm,mocking,Swift,Xcode,Unit Testing,Mvvm,Mocking,我在MVVM环境中使用Xcode为视图模型生成代码覆盖率时遇到了一个问题 我们的基本设置是视图控制器向视图模型发出请求,视图模型反过来调用与web服务对话的数据管理器上的方法 我提出了一种相当优雅的方法来测试视图模型,方法是创建一个伪数据管理器,将实际的数据管理器子类化,并覆盖VM调用的函数 问题是,要使其工作,VM必须是应用程序目标和测试目标的一部分。这样做的一个明显的副作用是,即使单元测试通过,也不会为属于两个或多个目标的项生成代码覆盖率。在项目中启用了代码覆盖率 以下是摘录的视图模型: i

我在MVVM环境中使用Xcode为视图模型生成代码覆盖率时遇到了一个问题

我们的基本设置是视图控制器向视图模型发出请求,视图模型反过来调用与web服务对话的数据管理器上的方法

我提出了一种相当优雅的方法来测试视图模型,方法是创建一个伪数据管理器,将实际的数据管理器子类化,并覆盖VM调用的函数

问题是,要使其工作,VM必须是应用程序目标和测试目标的一部分。这样做的一个明显的副作用是,即使单元测试通过,也不会为属于两个或多个目标的项生成代码覆盖率。在项目中启用了代码覆盖率

以下是摘录的视图模型:

import Foundation

class BoosViewModel: BaseViewModel {
    convenience override init()  {
        self.init(dataManager: BoosDataManager(), andModel: nil)
    }

    func getUnlinkedBoos(_ cardType: CardType) {
        (dataManager as! BoosDataManager).getUnlinkedBoos(cardType) { result, error in
            ...stuff happens here...
        }
    }
}
。。。和数据管理器

class BoosDataManager: DataManager {

    static let SharedInstance: BoosDataManager = {
        var manager = BoosDataManager()
        return manager
    }()

    func getUnlinkedBoos(_ cardType: CardType = .loyalty, completion: @escaping ((_ result: BoosModel?, _ error: NSError?) -> Void)) {
        ...stuff happens here...
    }
}
…还有测试

class BoosViewModelTests: XCTestCase {

    func testGetUnlinkedBoosHappyPath() {
        class FauxDataManager: BoosDataManager {
            override func getUnlinkedBoos(_ cardType: CardType = .loyalty, completion: @escaping ((_ result: BoosModel?, _ error: NSError?) -> Void)) {
                ...stuff happens here...
            }
        }

        let viewModel = BoosViewModel()
        let dataManager = FauxDataManager()

        viewModel.dataManager = dataManager
        viewModel.getUnlinkedBoos(.loyalty)
        XCTAssertTrue(testObserver.updated)
        XCTAssertEqual(testObserver.newViewModel.getBoos().count, 1)
    }
}
正如我前面提到的,这个场景中的单元测试成功完成,但是没有生成单元覆盖率

我有一些较旧的测试,我实际上创建了一个测试使用的外部假数据管理器类,被测试的类不是测试目标的一部分,覆盖率工作正常

这样做的缺点是,我必须创建多个数据管理器来处理其返回的特定案例。如果我不能封装这些类,我需要创建一组swift数据管理器,每个场景一个

这就是我提出内部类的原因

现在,如果我从测试目标中删除被测试的视图模型,问题就出现了。完成此操作后,我将@testable import BoosApp添加到单元测试中,以便可以解析测试中的视图模型。执行此操作时,会出现以下错误:

无法将类型为“BoosTests.BoosViewModelTests.(testGetUnlinkedBoosHappyPath()->())(FauxDataManager(1)”的值(0x11f673d18)强制转换为“Boos.BoosDataManager”(0x10444b128)。 8月30日20:43:01支付[19025]:无法将类型“BoosTests.BoosViewModelTests.(testGetUnlinkedBoosHappyPath()->())(FauxDataManager(1)”的值(0x11f673d18)强制转换为“Boos.BoosDataManager”(0x10444b128)。


我不确定我错过了什么。有没有办法让这个场景正常工作,或者我在测试代码之外创建了多个数据管理器?最终,我发现主要问题是视图模型和数据管理器不知何故被添加到了测试目标中。在将它们从测试目标中删除之后,我能够进行一些小的更改,并且一切都正常运行。仅供参考。

最终,我发现主要问题是视图模型和数据管理器以某种方式添加到了测试目标中。在将它们从测试目标中删除之后,我能够进行一些小的更改,并且一切都正常运行。仅供参考