Again we’ve seen something incredible. Although the main point of this series is that we want to reliably test async code in Swift, there is the other side benefit that surprisingly pops up. The tool we’ve cooked up for serializing asynchronous work in tests can also be used to make certain kinds of Xcode previews execute faster, and improve our ability to quickly iterate on features that make use of time-based asynchrony.
So, it’s all looking amazing, but I’m sure there’s a number of our viewers who are thinking that we have done some truly bizarre things in these past few episodes. We have completely altered the execution environment our tests run in, seemingly just so that we can get them passing. But doesn’t that mean there was something wrong with our features or tests in the first place? And doesn’t that mean that we aren’t actually testing reality since our apps do not operate on the main serial executor when run on our user’s devices.
Well, for the feature we built and the kinds of tests we wrote, neither is true. There is absolutely nothing wrong with our features or tests, and for the kinds of behavior that we are wanting to test, using the main serial executor is essentially equivalent to using default, global executor.
To explore this, we are going rebuild the feature from past episodes using only the tools from Combine. We aren’t going to use any fancy Swift concurrency tools. This will allow us to compare the two styles to see how they differ. In particular, we find that the Combine code is a little more verbose and annoying to work with, but overall looks roughly the same as the async version. The tests also look almost identical, however we will find that they pass deterministically, 100% of the time.
Let’s dig in.