简体   繁体   中英

Accessing class instance in XCTest using SwiftUI and @EnvironmentObject

I have a SwiftUI View hierarchy with an instance of a custom class injected using.environment() similar to the following:

struct ContentView: View {

  // Pointer to the AppStateController passed in .environment()
  @EnvironmentObject var appStateController: AppStateController

  var body: some View {
    VStack(spacing: 0) {
      TitleView()
        .modifier(TitleStyle())
        .environmentObject(appStateController)
      Spacer()
    }
  }

}


struct TitleView: View {

  @EnvironmentObject var appStateController: AppStateController

  var body: some View {

    Button(action: {
      self.appStateController.isPlaying.toggle()
    }, label: {
      if self.appStateController.isPlaying {
        Image(systemName: "stop.circle")
          .opacity(self.appStateController.isPlayable ? 1.0 : 0.5)
          .accessibility(label: Text("stop"))
      }
      else {
        Image(systemName: "play.circle")
          .opacity(self.appStateController.isPlayable ? 1.0 : 0.5)
          .accessibility(label: Text("play"))
      }
    })
  }

}

On the TitleView there are a bunch of buttons whose actions change @Published values in the appStateController. The buttons also change their label (icon) when tapped.

I am just getting started with UI unit testing and I've got as far as testing a button tap changes the icon (by searching for the button and inspecting it's accessibility label) and that works fine, but I'd also like to assert that the action actually does something by checking the appStateController.isPlaying boolean - effectively testing that my action: {} closure does what I need it to.

I can't seem to locate any documentation that tells me how, given a running app, I can find a reference, through the view hierarchy, to the injected appStateController and inspect the attributes therein. Is this possible and if so, does anyone know where I can find some documentation/blog articles on doing this?

In UI testing you test UI flows, not internal engine states, so you can verify that UI represents correct state (ie. engine finished correctly and UI updated correspondingly). For that purpose it needs to identify required UI elements and then check if desired present in runtime.

Here is possible approach. Tested with Xcode 11.4.

1) in code add accessibility identifiers

      if self.appStateController.isPlaying {
        Image(systemName: "stop.circle")
          .opacity(self.appStateController.isPlayable ? 1.0 : 0.5)
          .accessibility(identifier: "playing")       // identify state !!
          .accessibility(label: Text("stop"))
      }
      else {
        Image(systemName: "play.circle")
          .opacity(self.appStateController.isPlayable ? 1.0 : 0.5)
          .accessibility(identifier: "idle")
          .accessibility(label: Text("play"))        // identify state !!
      }

2) in XC test

    func testPlaying() throws {
        // UI tests must launch the application that they test.
        let app = XCUIApplication()
        app.launch()

        // UI manipulations to activate playback

        XCTAssertTrue(app.images["playing"].exists)
    }

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