Skip to content

How to Avoid 'Accessing StateObject's object without being installed on a View' Warnings in ViewInspector Tests?" #403

@lisafly2014

Description

@lisafly2014

To reproduce my issue, I created a simple sample with two files: ComplexView.swift and ComplexView+ViewModel.swift, along with a sample view unit test using ViewInspector. When I run the test, I see many warnings like:

"Accessing StateObject's object without being installed on a View. This will create a new instance each time.

Image

"

import SwiftUI

struct ComplexView: View {
@StateObject var viewModel: ViewModel

public init() {
    _viewModel = StateObject(wrappedValue: ViewModel())
}

#if DEBUG
public init(viewModel: ViewModel) {
self._viewModel = StateObject(wrappedValue: viewModel)
}
#endif

var body: some View {
    VStack {
        TextField("Name", text: $viewModel.name)
        Stepper("Number", value: $viewModel.number)
        Button("Validate") {
            viewModel.validate()
        }
        if viewModel.isValid {
            Text("Valid!").foregroundColor(.green)
        } else {
            Text("Invalid").foregroundColor(.red)
        }
    }
}

}

import SwiftUI

extension ComplexView {
@mainactor final class ViewModel: ObservableObject {

    // MARK: - Properties
    @Published var name: String = ""
    @Published var number: Int = 0
    @Published var isValid: Bool = false

    init() {}

    func validate() {
        isValid = !name.isEmpty && number > 0
    }
}

}

Then a simple unit test:
import XCTest
import SwiftUI
import ViewInspector

@mainactor
final class ComplexViewTests: XCTestCase {
func testComplexView() async throws {

    let viewModel = ComplexView.ViewModel()

    let view = ComplexView(viewModel: viewModel)
    try await ViewHosting.host(view) {
        let inspected = try view.inspect()
        let nameField = try inspected.find(ViewType.TextField.self)
        try nameField.setInput("Test")
        let stepper = try inspected.find(ViewType.Stepper.self)
        try stepper.increment()
        let button = try inspected.find(button: "Validate")
        try button.tap()
        let validText = try inspected.find(text: "Valid!")
        XCTAssertEqual(try validText.string(), "Valid!")
    }
}

}

When running this test, I get lots of warning:

Accessing StateObject's object without being installed on a View. This will create a new instance each time.

My view has both a default initializer and a debug/test initializer that accepts a view model, similar to the example above. I am using the async ViewHosting.host(_:whileHosted:) API from ViewInspector, but the warning still appears. Is there any way to avoid or suppress this warning when testing views with @StateObject using ViewInspector? Or is this a known limitation of SwiftUI and ViewInspector?

Any advice or workaround would be greatly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions