8

我正在研究如何对 SwiftUI 视图代码进行单元测试。

我有以下定义:

struct ContentView : View {
    var body: some View {

        Text("Hello World")
            .font(.title)
            .fontWeight(.bold) 
    }
}

我可以像这样测试它:

    func testBody() {
        let cv = ContentView()

        let body = cv.body

        XCTAssertNotNil(body)

        guard let text = body as? Text else { XCTFail(); return }

        XCTAssertEqual(Text("Hello World").font(.title).fontWeight(.bold), text)
    }

但是,一旦我想测试文本对齐方式,就会遇到问题:

生产代码:

struct ContentView : View {
    var body: some View {

        Text("Hello World")
            .font(.title)
            .fontWeight(.bold)
        .multilineTextAlignment(.leading)
    }
}

和测试代码:

    func testBody() {
        let cv = ContentView()

        let body = cv.body

        XCTAssertNotNil(body)

        guard let text = body as? Text else { XCTFail(); return }

        // COMPILER ERROR ON NEXT LINE
        XCTAssertEqual(Text("Hello World").font(.title).fontWeight(.bold).multilineTextAlignment(.leading), text)  
    }

...然后我收到以下编译器错误:

Cannot convert value of type 'Text' to expected argument type '_ModifiedContent<Text, _EnvironmentKeyWritingModifier<HAlignment>>'

如何测试Text结构的对齐方式?

4

2 回答 2

4

有两个扩展名.font(_:),即:

extension View {
    /// Sets the default font for text in this view.
    ///
    /// - Parameter font: The default font to use in this view.
    /// - Returns: A view with the default font set to the value you supply.
    public func font(_ font: Font?) -> Self.Modified<_EnvironmentKeyWritingModifier<Font?>>
}

extension Text {
    /// Sets the font to use when displaying this text.
    ///
    /// - Parameter font: The font to use when displaying this text.
    /// - Returns: Text that uses the font you specify.
    public func font(_ font: Font?) -> Text
}

当你在一个结构上执行方法 .font 时Text你会得到一个新的Text,带有应用的字体,因为它调用了重载font(_:)(创建一个具有更具体返回类型的重载方法总是可以的)。例如,当您在 a 上调​​用 font 方法时,Button返回类型为:

ModifiedContent<Button<Text>, _EnvironmentKeyWritingModifier<Font?>>

好吧,这不再是正常Button的,而是一个非常复杂的包装类型,因为它不像 Text 那样有自己的重载,所以它调用“正常”方法。

当你调用multilineTextAlignment你的Text实例时发生了什么?

extension View {

    /// Sets the alignment of multiline text in this view.
    ///
    /// - Parameter alignment: A value you use to align lines of text to the
    ///   left, right, or center.
    /// - Returns: A view that aligns the lines of multiline `Text` instances
    ///   it contains.
    public func multilineTextAlignment(_ alignment: HAlignment) -> Self.Modified<_EnvironmentKeyWritingModifier<HAlignment>>
}

没有重载Text,就像 font 方法一样。这意味着返回类型与新Text实例不同。现在我们陷入了一个复杂的时代。

幸运的是,content该复杂类型有一个属性。执行此操作时,您的测试将编译:

XCTAssertEqual(Text("Hello World").font(.title).fontWeight(.bold).multilineTextAlignment(.leading).content, text)

注意到content属性:)?

运行测试时,body变量不是 type Text,就像您期望的那样,而是另一种非常复杂的类型,这就是您的测试失败的原因。要通过测试,请执行以下操作:

func testBody() {
    let cv = ContentView()

    let body = cv.body

    XCTAssertNotNil(body)

    // Yuk!! Ugly cast, but don't know how to fix it since yeah, it is the type of the body...
    guard let text = body as? (_ModifiedContent<Text, _EnvironmentKeyWritingModifier<HAlignment>>) else { XCTFail(); return }

    // No compile error :) and a passing test!
    XCTAssertEqual(Text("Hello World").font(.title).fontWeight(.bold).multilineTextAlignment(.leading).content, text.content)
}
于 2019-06-06T18:34:09.663 回答
1

您可以使用ViewInspector库对 SwiftUI 层次结构进行单元测试:

let view = ContentView()
let string = try view.inspect().text().string()
XCTAssertEqual(string, "Hello, world!")
于 2019-11-23T14:51:02.303 回答