Search code examples
swiftuixcode-ui-testingxcuitest

How to run UI tests for SwiftUI App lifecycle apps with different environments?


I'm trying to figure out how to run UI tests for a SwiftUI app that is using the "SwiftUI App lifecycle" with preview data - in particular some data for CoreData, but it might be more general.

With the SwiftUI App lifecycle, we know have "main" entry points like:

@main
struct MyApp: App {
  let persistenceController = PersistenceController.shared
  
  var body: some Scene {
    WindowGroup {
      ContentView()
        .environment(\.managedObjectContext, persistenceController.container.viewContext)
    }
  }
}

where PersistenceController is a struct that is managing the CoreData stuff (with this example created by Apple's template if you just make a new App and select "use CoreData"). I have written an extension with a bunch of preview data that can easily be loaded in PreviewProviders just by setting a different managedObjectContext with .environment() on the view code, etc. for use while developing the UI code.

Is there a way to make this preview data available inside a UI test? We usually have UI test code that looks sort of like:

class MyUITests: XCTestCase {
  
  var app: XCUIApplication!
  
  override func setUpWithError() throws {
    app = XCUIApplication()
    app.launch()
    continueAfterFailure = false
  }
  
  func testTabBarButtonsAndNavTitles() throws {
    let tabBar = app.tabBars["Tab Bar"]

    let loadAndGoTabBarButton = tabBar.buttons["Load and go"]
    loadAndGoTabBarButton.tap()
    XCTAssert(app.navigationBars["Load a thing and do it"].exists)
  }

Is there a way I can tell the XCUIApplication() to use a different managedObjectContext value in the environment call when it starts?

If not - it seems like the only way to test some of the SwiftUI elements would be to have the UI test function first "tap around" and enter a bunch of preview data, but this is really cumbersome. It would be better if the app could start up for testing with some saved data.

Apologies for not providing a fully runnable piece of code to illustrate this, but it would require a lot of infrastructure and boilerplate, etc. Thanks for any thoughts on this!


Solution

  • Options you might want to try:

    • You can pass arguments to the application (via XCUIApplication.launchArguments). You can read the arguments from Process.arguments or UserDefaults (as defaults can be specified as arguments).
    • You could populate a test database on a device then dump the application data container (see https://www.codementor.io/@paulzabelin/xcode-app-data-suni6p4ma for a tutorial) and use that container in your UI tests.

    Specifying a test suites application data