【问题标题】:Mocking view model in swiftui with combine在swiftui中使用组合模拟视图模型
【发布时间】:2021-07-28 17:56:13
【问题描述】:

有没有办法模拟使用 SwiftUI 和 Combine 的应用程序的视图模型?我总能找到关于视图模型使用的模拟服务但从不模拟视图模型本身的文章。

我尝试为每个视图模型创建协议。问题:@Published 包装器不能在协议中使用。好像没有解决办法……

感谢您的帮助

【问题讨论】:

    标签: swiftui combine


    【解决方案1】:

    使用@ObservableObject@StateObject 等协议类型将不起作用。继承可能是一种解决方案(就像 Jake 建议的那样),或者您可以使用通用解决方案。

    protocol ContentViewModel: ObservableObject {
        var message: String { get set }
    }
    

    您的视图模型会很简单。

    final class MyViewModel: ContentViewModel {
        
        @Published var message: String
        init(_ message: String = "MyViewModel") {
            self.message = message
        }
    }
    

    另一方面,使用受约束的泛型您的视图会更复杂。

    struct ContentView<Model>: View where Model: ContentViewModel {
        @ObservedObject
        var viewModel: Model
        
        var body: some View {
            VStack {
                Text(viewModel.message)
                Button("Change message") {
                    viewModel.message = "?"
                }
            }
        }
    }
    

    缺点是在使用视图时必须定义通用的具体类型——继承可以避免这种情况。

    // your mock implementation for testing
    final class MockViewModel: ContentViewModel {
        @Published var message: String = "Mock View Model"
    }
    
    let sut = ContentView<MockViewModel>(viewModel: MockViewModel())
    

    【讨论】:

    • 谢谢。我用协议做到了这一点。唯一的问题(除了复杂性)是您无法访问属性的发布者。模拟视图模型背后的想法不仅用于测试,还用于注入(以及用于预览的模拟视图模型)
    • 是的,这是不可能的,我想不出解决办法。您可以将您的协议类型定义为 Publisher Published&lt;String&gt;.Publisher,但我认为走这条路没有任何好处,只会增加复杂性。
    • 谢谢。我将只考虑 swift 的限制。感谢您的建议。
    【解决方案2】:

    如果视图模型只是一个模型,您可能根本不应该模拟它,而是模拟修改视图模型的东西。如果您的视图模型实际上拥有自己更新的功能和事物,那么您可能希望直接模拟它。在这种情况下,您可以使用协议来模拟视图模型。它看起来有点像这样。

    protocol ViewModel {
        var title: String { get set }
    }
    
    class MyViewModel: ViewModel {
        @Published var title: String = "Page title"
    }
    
    class MockViewModel: ViewModel {
        @Published var title: String = "MockPage title"
    }
    

    但是,这可能是一个我更喜欢继承类而不是遵守协议的实例。然后我会覆盖模拟类的功能。

    open class ViewModel {
        @Published var title: String
    
        open fun getPageTitle() {
            title = "This is the page title"
        }
    }
    
    class MockViewModel: ViewModel {
        override fun getPageTitle() {
            title = "some other page title"
        }
    }
    

    无论哪种方式都可以。如果您的视图模型也有功能,那么继承方式在您的测试套件中就不那么冗长了。

    【讨论】:

      猜你喜欢
      • 2020-01-31
      • 2021-06-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      • 1970-01-01
      • 2016-12-28
      • 1970-01-01
      相关资源
      最近更新 更多