One of the first enhancement requests we received for Swift Testing was the ability to test for precondition failures and other critical failures that terminate the current process when they occur.
[…]
This proposal introduces new overloads of the
#expect()
and#require()
macros that take, as an argument, a closure to be executed in a child process.
When called, these macros spawn a new process using the relevant
platform-specific interface (posix_spawn()
,CreateProcessW()
, etc.), call
the closure from within that process, and suspend the caller until that process
terminates. The exit status of the process is then compared against a known
value passed to the macro, allowing the test to pass or fail as appropriate.
I had heard talk about changing the behavior of preconditions and assertions during tests so that they would signal back to the testing system instead of actually terminating the process. This is a much more ambitious solution and should be more widely useful. For example, I’ve written tests to reproduce crashes that are triggered by a bug, but I can’t leave these enabled with XCTest because they’ll crash the whole testing system.
Exit tests cannot capture any state originating in the parent process or from the enclosing lexical context.
This is an unsurprising limitation. I’m guessing that it won’t be much of a problem, but I’m not sure without trying it.
It is often interesting to examine what is written to the standard output and
standard error streams by code running in an exit test. Callers can request that
either or both stream be captured and included in the result of the call to
#expect(exitsWith:)
or#require(exitsWith:)
.[…]
The technical constraints preventing recursive exit test invocation can be resolved if there is a need to do so. However, we don’t anticipate that this constraint will be a serious issue for developers.
Previously: