返回顶部
f

fosmvvm-ui-tests-generatorFOSMVVM UI测试生成器

Generate UI tests for FOSMVVM SwiftUI views using XCTest and FOSTestingUI. Covers accessibility identifiers, ViewModelOperations, and test data transport.

作者: admin | 来源: ClawHub
源自
ClawHub
版本
V 2.0.6
安全检测
已通过
550
下载量
免费
免费
0
收藏
概述
安装方式
版本历史

fosmvvm-ui-tests-generator

FOSMVVM UI 测试生成器

为 FOSMVVM 应用中的 ViewModelView 生成全面的 UI 测试。

概念基础

完整架构上下文请参见 FOSMVVMArchitecture.md | OpenClaw 参考

FOSMVVM 中的 UI 测试遵循特定模式,利用:

  • - FOSTestingUI 框架提供测试基础设施
  • ViewModelOperations 验证业务逻辑是否被调用
  • 无障碍标识 定位 UI 元素
  • 测试数据传输器 将操作桩传递给应用

┌─────────────────────────────────────────────────────────────┐
│ UI 测试架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 测试文件 (XCTest) 被测试应用 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MyViewUITests │ │ MyView │ │
│ │ │ │ │ │
│ │ presentView() ───┼─────────────►│ 显示带有桩数据 │ │
│ │ 使用桩 VM │ │ 的视图 │ │
│ │ │ │ │ │
│ │ 通过标识交互 ────┼─────────────►│ 带有 .uiTestingId│ │
│ │ │ │ 的 UI 元素 │ │
│ │ │ │ │ │
│ │ 断言 UI 状态 │ │ .testData────────┼──┐ │
│ │ │ │ 传输器 │ │ │
│ │ │ └──────────────────┘ │ │
│ │ viewModelOps() ◄─┼─────────────────────────────────────┘ │
│ │ 验证调用 │ 桩操作 │
│ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

核心组件

1. 基础测试用例类

每个项目应有一个继承自 ViewModelViewTestCase 的基础测试用例:

swift
class MyAppViewModelViewTestCase:
ViewModelViewTestCase, @unchecked Sendable {

@MainActor func presentView(
configuration: TestConfiguration,
viewModel: VM = .stub(),
timeout: TimeInterval = 3
) throws -> XCUIApplication {
try presentView(
testConfiguration: configuration.toJSON(),
viewModel: viewModel,
timeout: timeout
)
}

override func setUp() async throws {
try await super.setUp(
bundle: Bundle.main,
resourceDirectoryName: ,
appBundleIdentifier: com.example.MyApp
)

continueAfterFailure = false
}
}

关键点:

  • - 泛型于 ViewModel 和 ViewModelOperations
  • 使用项目特定配置包装 FOSTestingUI 的 presentView()
  • 设置 bundle 和应用 bundle 标识符
  • continueAfterFailure = false 在失败时立即停止测试

2. 单个 UI 测试文件

每个 ViewModelView 对应一个 UI 测试文件。

对于带有操作的视图:

swift
final class MyViewUITests: MyAppViewModelViewTestCase {
// UI 测试 - 验证 UI 状态
func testButtonEnabled() async throws {
let app = try presentView(viewModel: .stub(enabled: true))
XCTAssertTrue(app.myButton.isEnabled)
}

// 操作测试 - 验证操作被调用
func testButtonTap() async throws {
let app = try presentView(configuration: .requireSomeState())
app.myButton.tap()

let stubOps = try viewModelOperations()
XCTAssertTrue(stubOps.myOperationCalled)
}
}

private extension XCUIApplication {
var myButton: XCUIElement {
buttons.element(matching: .button, identifier: myButtonIdentifier)
}
}

对于不带操作的视图(仅显示):

使用空桩操作协议:

swift
// 在你的测试文件中
protocol MyViewStubOps: ViewModelOperations {}
struct MyViewStubOpsImpl: MyViewStubOps {}

final class MyViewUITests: MyAppViewModelViewTestCase {
// 仅 UI 测试 - 无操作验证
func testDisplaysCorrectly() async throws {
let app = try presentView(viewModel: .stub(title: Test))
XCTAssertTrue(app.titleLabel.exists)
}
}

何时使用每种方式:

  • - 带操作:执行操作的交互式视图(表单、调用 API 的按钮等)
  • 不带操作:仅显示视图(卡片、详情视图、静态内容)

3. XCUIElement 辅助扩展

与 UI 元素交互的常用辅助方法:

swift
extension XCUIElement {
var text: String? {
value as? String
}

func typeTextAndWait(_ string: String, timeout: TimeInterval = 2) {
typeText(string)
_ = wait(for: \.text, toEqual: string, timeout: timeout)
}

func tapMenu() {
if isHittable {
tap()
} else {
coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
}
}
}

4. 视图要求

对于带有操作的视图:

swift
public struct MyView: ViewModelView {
#if DEBUG
@State private var repaintToggle = false
#endif

private let viewModel: MyViewModel
private let operations: MyViewModelOperations

public var body: some View {
Button(action: doSomething) {
Text(viewModel.buttonLabel)
}
.uiTestingIdentifier(myButtonIdentifier)
#if DEBUG
.testDataTransporter(viewModelOps: operations, repaintToggle: $repaintToggle)
#endif
}

public init(viewModel: MyViewModel) {
self.viewModel = viewModel
self.operations = viewModel.operations
}

private func doSomething() {
operations.doSomething()
toggleRepaint()
}

private func toggleRepaint() {
#if DEBUG
repaintToggle.toggle()
#endif
}
}

对于不带操作的视图(仅显示):

swift
public struct MyView: ViewModelView {
private let viewModel: MyViewModel

public var body: some View {
VStack {
Text(viewModel.title)
Text(viewModel.description)
}
.uiTestingIdentifier(mainContent)
}

public init(viewModel: MyViewModel) {
self.viewModel = viewModel
}
}

关键模式(对于带操作的视图):

  • - @State private var repaintToggle = false 用于触发测试数据传输
  • 在 DEBUG 中使用 .testDataTransporter(viewModelOps:repaintToggle:) 修饰符
  • 每次操作调用后调用 toggleRepaint()
  • operations 作为属性从 viewModel.operations 存储

仅显示视图:

  • - 不需要 repaintToggle
  • 不需要 .testDataTransporter() 修饰符
  • 只需在要测试的元素上添加 .uiTestingIdentifier()

ViewModelOperations:可选

并非所有视图都需要 ViewModelOperations:

需要操作的视图:

  • - 带有提交/取消操作的表单
  • 调用业务逻辑或 API 的视图
  • 触发应用状态变化的交互式视图
  • 用户发起的异步操作视图

不需要操作的视图:

  • - 仅显示卡片或详情视图
  • 静态内容视图
  • 纯导航容器
  • 仅渲染数据的服务器托管视图

对于不带操作的视图:

在 ViewModel 旁边创建一个空操作文件:

swift
// MyDisplayViewModelOperations.swift
import FOSMVVM
import Foundation

public protocol MyDisplayViewModelOperations: ViewModelOperations {}

#if canImport(SwiftUI)
public final class MyDisplayViewStubOps: MyDisplayViewModelOperations, @unchecked Sendable {
public init() {}
}
#endif

然后在测试中使用:

swift
final class MyDisplayViewUITests: MyAppViewModelViewTestCase<
MyDisplayViewModel,
MyDisplayViewStubOps

{


// 仅测试 UI 状态,无操作验证
}

视图本身不需要:

  • - repaintToggle 状态
  • .testDataTransporter() 修饰符
  • operations 属性
  • toggleRepaint() 函数

只需在要验证的元素上添加 .uiTestingIdentifier()。

测试类别

UI 状态测试

验证 UI 根据 ViewModel 状态正确显示

标签

skill ai

通过对话安装

该技能支持在以下平台通过对话安装:

OpenClaw WorkBuddy QClaw Kimi Claude

方式一:安装 SkillHub 和技能

帮我安装 SkillHub 和 fosmvvm-ui-tests-generator-1776420040 技能

方式二:设置 SkillHub 为优先技能安装源

设置 SkillHub 为我的优先技能安装源,然后帮我安装 fosmvvm-ui-tests-generator-1776420040 技能

通过命令行安装

skillhub install fosmvvm-ui-tests-generator-1776420040

下载

⬇ 下载 fosmvvm-ui-tests-generator v2.0.6(免费)

文件大小: 11.86 KB | 发布时间: 2026-4-17 20:03

v2.0.6 最新 2026-4-17 20:03
Initial ClawHub release

Archiver·手机版·闲社网·闲社论坛·羊毛社区· 多链控股集团有限公司 · 苏ICP备2025199260号-1

Powered by Discuz! X5.0   © 2024-2025 闲社网·线报更新论坛·羊毛分享社区·http://xianshe.com

p2p_official_large
返回顶部