I am using Xcode 15.0.1 to build a swift package, this is not a project. I am getting a failure when the wait
in the test below times out and it does not run the closure on the test.on(\.didAppear)
line. FYI I have this working in other tests in my package.
In my Package.swift file I have the following.
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v16)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(name: "MyLibrary", targets: ["MyLibrary"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
.package(url: "https://github.com/realm/SwiftLint.git", from: "0.53.0"),
.package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.9.8")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "MyLibrary",
dependencies: [],
resources: [
.process("Media.xcassets")
],
plugins: [
.plugin(name: "SwiftLintPlugin", package: "SwiftLint")
]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary", "ViewInspector"])
]
)
Here is a code that fails to run. It is the same basic structure as the code in my package.
import SwiftUI
import ViewInspector
import XCTest
@testable import MyLibrary
final class MyTests: XCTestCase {
struct TestView: View {
@Binding var text: String
@State var display: String = ""
internal var didAppear: ((Self) -> Void)?
init(text: String) {
self._text = Binding<String>.init(wrappedValue: text)
}
var body: some View {
HStack {
Text("Hello")
Text(display)
}
.onChange(of: text, perform: { value in
self.display = value
})
}
}
func testTest() throws {
var test = TestView(text: "")
let exp = test.on(\.didAppear) { view in
try view.hStack().callOnChange(newValue: "World")
XCTAssertNotNil(try view.hStack().find(text: "World"))
}
ViewHosting.host(view: test)
wait(for: [exp], timeout: 1.0) // <- Fails here with a timeout
}
}
I use the onChange
in the test above to use the same update mechanism as in my code.
I found the answer to my question. There is a line missing in the var body: some View
variable. It should include .onAppear { self.didAppear?(self) }
. Therefore the TestView should be:
struct TestView: View {
@Binding var text: String
@State var display: String = ""
internal var didAppear: ((Self) -> Void)?
init(text: String) {
self._text = Binding<String>.init(wrappedValue: text)
}
var body: some View {
HStack {
Text("Hello")
Text(display)
}
.onChange(of: text, perform: { value in
self.display = value
})
.onAppear { self.didAppear?(self) } // <- this was missing
}
}
Otherwise exp
is never set and the wait
function has nothing to "wait" for.