christopherthiebaut.com

How to exclude SwiftUI Previews From Code Coverage Percentage

TL;DR: Create a new scheme and compiler flag for your tests.

THE PROBLEM:

SwiftUI previews are great and can give you fantastic iteration speed when changing your app’s views. But what about the test coverage that gives you a high level of confidence to iterate quickly on your app itself? While you can test SwiftUI views by digging into their internals (not recommended as those tests may break in future SwiftUI versions) or with high level XCUI tests, you should not spend time unit testing SwiftUI previews because they’re not part of your app’s code.

The fact that SwiftUI previews generally are defined inline in the file with the view is great for development speed, but starts to become a problem if you use Xcode’s code coverage reports to track and ensure that you’re maintaining a high level of test coverage for your project. SwiftUI Previews show up as uncovered code, and suddenly you no longer expect to see code coverage reported at or near 100% and it becomes easier to let code coverage slip for actual production code. Alternatively, you may waste time digging in to find what code is uncovered — and repeatedly find out that it’s just previews instead of “real” code.

THE SOLUTION:

Fortunately, there’s a solution and it’s quick and easy. We just have to create a new build configuration for our tests that does not build the previews. So we start of with something like this:

image of a project with 79.5% test coverage

Since this (relatively simple) app was created with TDD, the reported test coverage is relatively good, but deceptively low due to SwiftUI previews.

To create a new configuration, go to the Info tab of your project settings and duplicate the Debug configuration.

GIF showing duplicating the debug configuration

Believe it or not, you’re already almost done. Next, we need to add an active compilation condition (a compiler flag to go along with the new scheme.

GIF showing creating an active compilation condition

Now, set the new build configuration as the configuration for your tests:

GIF showing how to set build configuration for tests

At this point, all you have left to do is surround your SwiftUI previews with the same #if DEBUG compiler directives that were the default when SwiftUI was brand new (back then, it was because the compiler wasn’t quite so good at optimizing them out of release builds). Alternatively, you could surround them with #if !TESTING. Either way, it’s important to make sure you have at least one application level test (like an XCUITest) that ensures you can’t use these compiler directives to exclude any production code without your test build failing.

For example:

#if DEBUG
struct RestartOverlay_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            RestartOverlay(gameEnd: .draw, restart: {})
                .border(Color.orange, width: 2)
            RestartOverlay(gameEnd: .winner(.o), restart: {})
                .border(Color.orange, width: 2)
            RestartOverlay(gameEnd: .winner(.x), restart: {})
                .border(Color.orange, width: 2)
        }
    }
}
#endif

Thanks for reading, I hope this was helpful and informative.

Tagged with: