Reputation: 1426
I am trying to write unit tests for SwiftUI views but finding zero resources on the web for how to go about that.
I have a view like the following
struct Page: View {
@EnvironmentObject var service: Service
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack {
Text("Some text"))
.font(.body)
.navigationBarTitle(Text("Title")))
Spacer(minLength: 100)
}
}
}
}
}
I started writing a test like this
func testPage() {
let page = Page().environmentObject(Service())
let body = page.body
XCTAssertNotNil(body, "Did not find body")
}
But then how do I get the views inside the body? How do I test their properties? Any help is appreciated.
Update: As a matter of fact even this doesn't work. I am getting the following runtime exception
Thread 1: Fatal error: body() should not be called on ModifiedContent<Page,_EnvironmentKeyWritingModifier<Optional<Service>>>.
Upvotes: 11
Views: 6919
Reputation: 10791
There is a framework created specifically for the purpose of runtime inspection and unit testing of SwiftUI views: ViewInspector
You can extract your custom views to verify the inner state, trigger UI-input side effects, read the formatted text values, assure the right text styling is applied, and much more:
// Side effect from tapping on a button
try sut.inspect().find(button: "Close").tap()
let viewModel = try sut.inspect().view(CustomView.self).actualView().viewModel
XCTAssertFalse(viewModel.isDialogPresented)
// Testing localization + formatting
let sut = Text("Completed by \(72.51, specifier: "%.1f")%")
let string = try sut.inspect().text().string(locale: Locale(identifier: "es"))
XCTAssertEqual(string, "Completado por 72,5%")
The test for your view could look like this:
func testPage() throws {
let page = Page().environmentObject(Service())
let string = try page.inspect().navigationView().scrollView().vStack().text(0).string()
XCTAssertEqual(string, "Some text")
}
Upvotes: 11
Reputation: 20980
Update: Let's all try using the ViewInspector library by nalexn!
Original reply:
Until Apple
a) designs testability into SwiftUI, and
b) exposes this testability to us,
we're screwed, and will have to use UI Testing in place of unit testing… in a complete inversion of the Testing Pyramid.
Upvotes: 8