Supporting Unit Tests for View code (esp. SwiftUI)

I just had a great time in the 'Testing and Continuous Integration lab' trying to put in a voice for proper support for unit testing View code in XCTest. I'd like to share the FB ticket with my suggestion / wishes on that topic. Its based on what I learned from my iOS projects in the last years where I made an effort to properly do automated testing of View code. Would love to have a chat about the topic :)



FB7760343: Supporting Unit Tests for View code (esp. SwiftUI)

I'd like to be able to unit test View code (especially SwiftUI Views).

Compared to UI tests, where you test all the app built together in a system black box test; I'd like to put a single View in the spotlight, configure it for a specific case programatially (like plugging in a stubbed communication service that returns an error) and assert that it does the right thing in this situation.

The goal is to be able to test hundreds of cases very quickly / in mere seconds instead of having to wait minutes/hours to run a complex UI scenario. So you can re-run your unit tests after a little change and have a confident feedback in mere seconds that your code still works as expected. Including the parts of the code that are purely visual.

Here is a simplified example project to show what I'd like to be able to do in a very simplified fashion:
https://github.com/ralfebert/ExampleAppSwiftUIViewTesting
This is just a View with a button that increments a counter value.
The unit test target ExampleAppTests contains a reference image that shows what the view should look like and uses the Snap.swift swift package (https://github.com/skyweb07/Snap.swift) to check that the View draws correctly.
If the View doesn't do what it's expected to, you can diff the images to find out what's going wrong (see the ExampleAppSwiftUIViewTesting README.md page for an impression what that looks like).

It would be awesome if XCTest would have an API for such image comparisions built in (something like XCTAssertImage so to speak) with Xcode providing tooling to show-the-diff/update reference images.

Also, it would be great if you could capture such images without putting the View actually on the screen for faster run times (with SwiftUI I had to put the UIHostingController on the UIWindow to get a image to compare).

Also, it would be great if there would be an 'XCUIElement'-like API for unit tests that could be used to simulate events (like, tap on the button and then check the value increments correctly).

Last but not least, the reference images are a great tool to assert the state of an UI, but if you have hundreds of them, it gets a bit slow to compare the images; also it takes an effort to keep them up-to-date. I wonder, what could be an alternative to capture the essence of a View rendering? Maybe something like a textual representation of the UI that could be very quickly compared to detect an unexpected change. Or an API to drill down the View tree and look for certain things.

Maybe this could work together with the SwiftUI previews. I sometimes wonder if there could be a button in the preview "Add Unit Test" that would capture a visual assertion (as image or in some other form) and create a unit test that checks that it still works as expected.


Replies

I'll add one more use case:
  • being able to generate "App Store screenshots" with multiple devices/languages easily by attaching them to the tests (injecting values are so easy with SwiftUI)

Hey! We’re having trouble viewing that Feedback Assistant report — can you double-check that it’s the correct ID, or maybe file a new one? Thanks!
It would be good if Apple explain how write unit tests for Swift UI and Combine
@Developer Tools Engineer: I double checked, it's FB7760343 and it's shown as sent in the feedback assistant. I filed it for the XCTest project, is this the correct place?
@ralf.ebert thanks for sending us this feedback, I can see it now!
putting +1 for this as this feature is really required with Dark Mode variants and when in active development changing layout in common code can effect other areas of app