Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing in SwiftUI

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>>>.
like image 526
user1366265 Avatar asked Oct 04 '19 14:10

user1366265


People also ask

How do I write unit test cases in Xcode?

Enable Unit Tests in Xcode ProjectWhile creating a new Project, click the checkbox “Include Unit Tests”, “Include UI Tests”. Once created, you can able to see a folder in the project called “ProjectNameTests” and the XCode already creates a default test case class with a template generated to start working with.

What is SwiftUI good for?

SwiftUI helps you build great-looking apps across all Apple platforms with the power of Swift — and surprisingly little code. You can bring even better experiences to everyone, on any Apple device, using just one set of tools and APIs.


2 Answers

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")
}
like image 121
nalexn Avatar answered Sep 20 '22 13:09

nalexn


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.

like image 35
Jon Reid Avatar answered Sep 17 '22 13:09

Jon Reid