简体   繁体   中英

Is it possible to UI Test individual Swift UI components?

I am super new to Swift and SwiftUI and I have started a new project using SwiftUI. I have some experience in other component based libraries for the web and I wanted a way to use the same pattern for iOS development.

Is there a way to ui test individual components in SwiftUI? For example, I have created a Map component that accepts coordinates and renders a map and I want to test this map individually by making the app immediately render the component. Here is my code and test code at the moment:

// App.swift (main)
// Map is not rendered yet

@main
struct PicksApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// MyMap.swift
struct MyMap: View {

    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(
            latitude: 25.7617,
            longitude: 80.1918
        ),
        span: MKCoordinateSpan(
            latitudeDelta: 10,
            longitudeDelta: 10
        )
    )

    var body: some View {
        Map(coordinateRegion: $region)
    }
}

struct MyMap_Previews: PreviewProvider {
    static var previews: some View {
        MyMap()
    }
}

// MyMapUITests.swift
class MyMapUITests: XCTestCase {
    func testMapExists() throws {
        let app = XCUIApplication()
        app.launch()

        let map = app.maps.element
        XCTAssert(map.exists, "Map does not exist")
    }
}

Is it possible to tell UI Test framework to only test one component instead of launching the entire app and making me navigate between each view before I am able to get to my view?

For example, in my case, there is going to be a login view when the app opens for the first time (which is every time from perspective of ui testing) and the map view can be located inside the app somewhere. I want to be able to test only the map view without testing end-to-end user experience.

One approach you could take is to transform your app into a catalog one if some environment variables are found. For this you'll have to keep a fixed collection of views to use as the root of the app:

@main
struct PicksApp: App {
    static let viewBuilders: [String: () -> AnyView] = [
        "ContentView": { AnyView(ContentView()) },
        "MyMap": { AnyView(MyMap()) }]
    
    var body: some Scene {
        WindowGroup {
            if let viewName = ProcessInfo().customUITestedView,
               let viewBuilder = Self.viewBuilders[viewName] {
                viewBuilder()
            } else {
                AnyView(ContentView())
            }
        }
    }
}

Here's the ProcessInfo helper method:

extension ProcessInfo {
    var customUITestedView: String? {
        guard environment["MyUITestsCustomView"] == "true" else { return nil }
        return environment["MyCustomViewName"]
    }
}

With the above changes, the UI test needs only two more lines of code - the enviroment preparation:

func testMapExists() throws {
    let app = XCUIApplication()
    app.launchEnvironment["MyUITestsCustomView"] = "true"
    app.launchEnvironment["MyCustomViewName"] = "MyMap"
    app.launch()
    
    let map = app.maps.element
    XCTAssert(map.exists, "Map does not exist")
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM