如何在Swift包中为UIViewRepresentable编写测试



我正试图为我的项目创建一个包含一些共享UI元素的swift包,我想为它编写一些测试

这是视图之一

//
//  TextField.swift
//  Test
//
//  Created by Development on 05/03/2021.
//
#if !os(macOS)
import Combine
import SwiftUI
import UIKit
public struct TextFieldRepresentable: UIViewRepresentable {
@Binding var text: String
@Environment(.isSecureField) var isSecureField: Bool
@Environment(.isFirstResponder) var isFirstResponder: Binding<Bool>?
@Environment(.placeholder) var placeholder: String?
@Environment(.keyboardType) var keyboardType: UIKeyboardType
@Environment(.returnKeyType) var returnKeyType: UIReturnKeyType
@Environment(.textContentType) var contentType: UITextContentType?
@Environment(.autoCapitalisation) var autoCapitalisation: Bool
@Environment(.autoCorrection) var autoCorrection: Bool
@Environment(.font) var font: Font?
let onCommit: (() -> Bool)?

public init( text: Binding<String>,  onCommit: (() -> Bool)? = nil ) {
_text = text
self.onCommit = onCommit
}

public func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
syncValues(context: context, textField: textField)
return textField
}
public func updateUIView(_ uiView: UITextField, context: Context) {
syncValues(context: context, textField: uiView)
DispatchQueue.main.async {
if let isFirstResponderValue = isFirstResponder?.wrappedValue {
switch isFirstResponderValue {
case true:
uiView.becomeFirstResponder()
case false:
uiView.resignFirstResponder()
}
}
}
}
private func syncValues(context _: Context, textField: UITextField) {
textField.text = text
textField.isSecureTextEntry = isSecureField
textField.textContentType = contentType
textField.keyboardType = keyboardType
textField.placeholder = placeholder
textField.font = UIFont.preferredFont(from: font)
textField.autocapitalizationType = autoCapitalisation ? .sentences : .none
textField.returnKeyType = returnKeyType
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textField.autocorrectionType = autoCorrection ? .yes : .no
}
public func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
public class Coordinator: NSObject, UITextFieldDelegate {
private let parent: TextFieldRepresentable
init(parent: TextFieldRepresentable) {
self.parent = parent
}
public func textFieldShouldReturn(_: UITextField) -> Bool {
return parent.onCommit?() ?? true
}
public func textFieldDidChangeSelection(_ textField: UITextField) {
parent.$text.wrappedValue = textField.text ?? ""
}
public func textFieldDidBeginEditing(_: UITextField) {
parent.isFirstResponder?.wrappedValue = true
}
}
}
#endif

我想测试一下,当我应用修饰符时,底层UIView会对更改做出响应。有没有办法在SPM单元测试中加载这个视图,并获得底层的UITextField,这样我就可以看到修改器工作了?

我尝试过使用Intrspect和ViewInparticle,但没有成功。

此外,我还注意到,在SPM中,即使我构建了View,makeUIView(context:context(->UITextFieldupdateUIView(_uiView:UITextField,context:context(函数永远不会被调用。我在iOS项目的单元测试中尝试了同样的方法,并且正确地调用了函数。检查下方的代码

import Foundation
import XCTest
import SwiftUI
@testable import UI
final class TextFieldRepresentableTests: XCTestCase {


var view:TextFieldRepresentable!
var controller:UIHostingController<TextFieldRepresentable>!
override func setUp() {
view = TextFieldRepresentable(text: .constant("Test"))
controller = UIHostingController(rootView: view)
controller.loadViewIfNeeded()
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = controller
window.makeKeyAndVisible()
}

func test_isSecureField_IsTrue_ShouldTextFieldBeSecure() {
view.makeUIView(context: UIViewRepresentableContext<TextFieldRepresentable>())

}
}

你在这里有点运气不好。

测试UIViewRepresentable创建的子视图需要实例化在模拟器实例(或设备(上下文中运行的UIWindow

为了实现上述目标,你需要有一个iOS应用程序,它被单元测试用作主机。不幸的是,目前SPM没有提供创建iOS应用程序的选项(不确定添加该选项的可行性(。

一个可能的解决方法是:

  1. 创建一个伪iOS应用程序
  2. 为该iOS应用程序创建测试目标
  3. 将包链接到测试目标,这假设您在包中构建了库目标而不是测试目标

上述内容也增加了很多摩擦,因为测试包更改需要每次进行新的提交,并在虚拟项目中获取更新的包。

因此,您可能需要在这里重新考虑您的设计测试策略。例如,您可以将所有UITextField登录提取到一个没有任何SwiftUI依赖项的适配器类中,并测试该类。

最新更新