使用 XCTest 访问自定义 UIView 中的后代控件

Posted

技术标签:

【中文标题】使用 XCTest 访问自定义 UIView 中的后代控件【英文标题】:Accessing descendant controls in custom UIView using XCTest 【发布时间】:2017-07-25 17:03:55 【问题描述】:

我遇到的问题是我有包含文本字段的可重用视图/控件。这些是带有自定义 UI 视图类的 xib 文件,如下所示:

import UIKit

@IBDesignable
public class CustomControl: UIControl 

  @IBOutlet public weak var textField: UITextField?
  public var contentView: UIView?

  required public init?(coder aDecoder: NSCoder) 
    super.init(coder: aDecoder)
    setupViewFromNib()
  

  override public init(frame: CGRect) 
    super.init(frame: frame)
    setupViewFromNib()
  

  override public func awakeFromNib() 
    super.awakeFromNib()
    setupViewFromNib()
  

  override public func prepareForInterfaceBuilder() 
    super.prepareForInterfaceBuilder()
    setupViewFromNib()
    contentView?.prepareForInterfaceBuilder()
  

  func setupViewFromNib() 
    guard let view = loadViewFromNib() else  return 
    guard let textField = self.textField else  return 
    view.frame = bounds
    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    addSubview(view)
    contentView = view
  

  func loadViewFromNib() -> UIView? 
    let selfType = type(of: self)
    let nibName = String(describing: selfType)
    return Bundle(for: selfType)
        .loadNibNamed(nibName, owner: self, options: nil)?
        .first as? UIView
  

这个自定义视图正在加载到 Storyboard 中,将在其中使用 Storyboard Interface Builder。

问题是 XCTest 似乎没有对这些视图的后代进行建模,所以当我尝试编写一个涉及在自定义视图的文本字段中输入文本的测试时,测试用错误:

元素和任何后代都没有键盘焦点。

目前的解决方法似乎是点击键盘上的按键,而不是使用typeText 方法。然而,这要慢得多(按键之间的长时间停顿),而且测试代码也更加繁琐。

想要的代码:

let app = XCUIApplication()
let view = app.otherElements["customView"]
let textField = view.textFields["textField"]
textField.tap()
textField.typeText("12345")

使用测试记录,我们得到如下结果:

let app = XCUIApplication()
let view = app.otherElements["customView"]
view.tap()
app.typeText("12345")

但是运行这个测试会导致上述错误。

编辑/工作测试变为:

let app = XCUIApplication()
let view = app.otherElements["customView"]
// view appears as a leaf with no descendants
view.tap() 
app.keys["1"].tap()
app.keys["2"].tap()
app.keys["3"].tap()
app.keys["4"].tap()
app.keys["5"].tap()

如果自定义视图包含多个控件,我也不相信这种解决方法仍然可行,比如对于生育控制日期,我希望更精细地控制我正在使用的自定义控件中的哪个字段。

是否有允许我访问自定义视图中的字段并可能使用typeText 方法的解决方案?

【问题讨论】:

你有一个可以重现的示例项目吗? 您还需要确保在文本字段上而不是在自定义视图本身上启用了可访问性 【参考方案1】:

问题已解决。正如 Titouan de Bailleul 所说,问题在于自定义视图的可访问性已启用,有效地隐藏了其后代文本字段。

将示例项目添加到 Github:

https://github.com/stuartwakefield/XibXCTestIssueSample

感谢蒂图安。

【讨论】:

解决方案现在不在 GitHub 上。

以上是关于使用 XCTest 访问自定义 UIView 中的后代控件的主要内容,如果未能解决你的问题,请参考以下文章

如何访问使用 Paintcode 生成的 UIView 自定义类中的图层?

用于单元测试 Objective-C XCTest 的 .m 文件中的访问变量定义

如何使自定义 UIView 可访问?

使用 Interface Builder 构建的自定义 UIView 可通过 Interface Builder 访问/定位

在 XCTest 测试中从情节提要加载的视图控制器中清空 UIView 快照(间歇性)

UIView.animateWithDuration:animations:completion: 在 XCTest 中被取消