在 swiftui 中使用 combine 模拟视图模型
Mocking view model in swiftui with combine
有没有办法模拟使用 SwiftUI 和 Combine 的应用程序的视图模型?我总是找到有关视图模型使用的模拟服务的文章,但从不模拟视图模型本身。
我尝试为每个视图模型创建协议。问题:@Published 包装器不能在协议中使用。似乎没有解决方案...
感谢您的帮助
如果视图模型只是一个模型,您可能根本不应该模拟它,而是模拟修改视图模型的东西。如果您的视图模型实际上拥有自我更新的功能和事物,那么您会想直接模拟它。在这种情况下,您可以使用协议来模拟视图模型。看起来有点像这样。
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"
}
然而,这可能是我更愿意继承 class 而不是遵守协议的一个例子。然后我会为模拟覆盖 class 的功能。
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"
}
}
任何一种方法都行得通。如果您的视图模型也具有功能,那么继承方式在您的测试套件中就不会那么冗长。
使用协议类型 @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())
有没有办法模拟使用 SwiftUI 和 Combine 的应用程序的视图模型?我总是找到有关视图模型使用的模拟服务的文章,但从不模拟视图模型本身。
我尝试为每个视图模型创建协议。问题:@Published 包装器不能在协议中使用。似乎没有解决方案...
感谢您的帮助
如果视图模型只是一个模型,您可能根本不应该模拟它,而是模拟修改视图模型的东西。如果您的视图模型实际上拥有自我更新的功能和事物,那么您会想直接模拟它。在这种情况下,您可以使用协议来模拟视图模型。看起来有点像这样。
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"
}
然而,这可能是我更愿意继承 class 而不是遵守协议的一个例子。然后我会为模拟覆盖 class 的功能。
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"
}
}
任何一种方法都行得通。如果您的视图模型也具有功能,那么继承方式在您的测试套件中就不会那么冗长。
使用协议类型 @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())