【发布时间】:2021-07-28 17:56:13
【问题描述】:
有没有办法模拟使用 SwiftUI 和 Combine 的应用程序的视图模型?我总能找到关于视图模型使用的模拟服务但从不模拟视图模型本身的文章。
我尝试为每个视图模型创建协议。问题:@Published 包装器不能在协议中使用。好像没有解决办法……
感谢您的帮助
【问题讨论】:
有没有办法模拟使用 SwiftUI 和 Combine 的应用程序的视图模型?我总能找到关于视图模型使用的模拟服务但从不模拟视图模型本身的文章。
我尝试为每个视图模型创建协议。问题:@Published 包装器不能在协议中使用。好像没有解决办法……
感谢您的帮助
【问题讨论】:
使用@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())
【讨论】:
Published<String>.Publisher,但我认为走这条路没有任何好处,只会增加复杂性。
如果视图模型只是一个模型,您可能根本不应该模拟它,而是模拟修改视图模型的东西。如果您的视图模型实际上拥有自己更新的功能和事物,那么您可能希望直接模拟它。在这种情况下,您可以使用协议来模拟视图模型。它看起来有点像这样。
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"
}
}
无论哪种方式都可以。如果您的视图模型也有功能,那么继承方式在您的测试套件中就不那么冗长了。
【讨论】: