66* Implementation: [ swiftlang/swift-testing #733 ] ( https://github.com/swiftlang/swift-testing/pull/733 ) , [ swiftlang/swift-testing #86 ] ( https://github.com/swiftlang/swift-testing/pull/86 )
77* Review: ([ pitch] ( https://forums.swift.org/... ) )
88
9+ ### Revision history
10+
11+ * ** v1** : Initial pitch.
12+ * ** v2** : Dropped 'Custom' prefix from the proposed API names (although kept the
13+ word in certain documentation passages where it clarified behavior).
14+
915## Introduction
1016
1117This introduces API which enables a custom ` Trait ` -conforming type to customize
@@ -190,21 +196,19 @@ these scoped access calls to only the traits which require it.
190196
191197I propose the following new APIs:
192198
193- - A new protocol ` CustomTestExecuting ` with a single required ` execute(...) `
194- method. This will be called to run a test, and allows the conforming type to
195- perform custom logic before or after.
196- - A new property ` customTestExecutor ` on the ` Trait ` protocol whose type is an
197- ` Optional ` value of a type conforming to ` CustomTestExecuting ` . A ` nil ` value
198- from this property will skip calling the ` execute(...) ` method.
199- - A default implementation of ` Trait.customTestExecutor ` whose value is ` nil ` .
200- - A conditional implementation of ` Trait.customTestExecutor ` whose value is
201- ` self ` in the common case where the trait type conforms to
202- ` CustomTestExecuting ` itself.
203-
204- Since the ` customTestExecutor ` property is optional and ` nil ` by default, the
205- testing library cannot invoke the ` execute(...) ` method unless a trait
206- customizes test behavior. This avoids the "unnecessarily lengthy backtraces"
207- problem above.
199+ - A new protocol ` TestExecuting ` with a single required ` execute(...) ` method.
200+ This will be called to run a test, and allows the conforming type to perform
201+ custom logic before or after.
202+ - A new property ` testExecutor ` on the ` Trait ` protocol whose type is an
203+ ` Optional ` value of a type conforming to ` TestExecuting ` . A ` nil ` value for
204+ this property will skip calling the ` execute(...) ` method.
205+ - A default implementation of ` Trait.testExecutor ` whose value is ` nil ` .
206+ - A conditional implementation of ` Trait.testExecutor ` whose value is ` self `
207+ in the common case where the trait type conforms to ` TestExecuting ` itself.
208+
209+ Since the ` testExecutor ` property is optional and ` nil ` by default, the testing
210+ library cannot invoke the ` execute(...) ` method unless a trait customizes test
211+ behavior. This avoids the "unnecessarily lengthy backtraces" problem above.
208212
209213Below are the proposed interfaces:
210214
@@ -215,12 +219,12 @@ Below are the proposed interfaces:
215219///
216220/// Types conforming to this protocol may be used in conjunction with a
217221/// ``Trait``-conforming type by implementing the
218- /// ``Trait/customTestExecutor-1dwpt `` property, allowing custom traits to
222+ /// ``Trait/testExecutor-714gp `` property, allowing custom traits to
219223/// customize the execution of tests. Consolidating common set-up and tear-down
220224/// logic for tests which have similar needs allows each test function to be
221225/// more succinct with less repetitive boilerplate so it can focus on what makes
222226/// it unique.
223- public protocol CustomTestExecuting : Sendable {
227+ public protocol TestExecuting : Sendable {
224228 /// Execute a function for the specified test and/or test case.
225229 ///
226230 /// - Parameters:
@@ -241,8 +245,8 @@ public protocol CustomTestExecuting: Sendable {
241245 /// When the testing library is preparing to run a test, it finds all traits
242246 /// applied to that test (including those inherited from containing suites)
243247 /// and asks each for the value of its
244- /// ``Trait/customTestExecutor-1dwpt `` property. It then calls this method on
245- /// all non-`nil` instances, giving each an opportunity to perform
248+ /// ``Trait/testExecutor-714gp `` property. It then calls this method
249+ /// on all non-`nil` instances, giving each an opportunity to perform
246250 /// arbitrary work before or after invoking `function`.
247251 ///
248252 /// This method should either invoke `function` once before returning or throw
@@ -259,42 +263,42 @@ public protocol CustomTestExecuting: Sendable {
259263public protocol Trait : Sendable {
260264 // ...
261265
262- /// The type of the custom test executor for this trait.
266+ /// The type of the test executor for this trait.
263267 ///
264268 /// The default type is `Never`, which cannot be instantiated. This means the
265- /// value of the ``customTestExecutor-1dwpt `` property for all traits with the
266- /// default custom executor type is `nil`, meaning such traits will not
269+ /// value of the ``testExecutor-714gp `` property for all traits with
270+ /// the default custom executor type is `nil`, meaning such traits will not
267271 /// perform any custom execution for the tests they're applied to.
268- associatedtype CustomTestExecutor : CustomTestExecuting = Never
272+ associatedtype TestExecutor : TestExecuting = Never
269273
270- /// The custom test executor for this trait, if any.
274+ /// The test executor for this trait, if any.
271275 ///
272- /// If this trait's type conforms to ``CustomTestExecuting ``, the default
273- /// value of this property is `self` and this trait will be used to customize
274- /// test execution. This is the most straightforward way to implement a trait
275- /// which customizes the execution of tests.
276+ /// If this trait's type conforms to ``TestExecuting ``, the default value of
277+ /// this property is `self` and this trait will be used to customize test
278+ /// execution. This is the most straightforward way to implement a trait which
279+ /// customizes the execution of tests.
276280 ///
277281 /// If the value of this property is an instance of a different type
278- /// conforming to ``CustomTestExecuting ``, that instance will be used to
279- /// perform custom test execution instead.
282+ /// conforming to ``TestExecuting ``, that instance will be used to perform
283+ /// test execution instead.
280284 ///
281285 /// The default value of this property is `nil` (with the default type
282286 /// `Never?`), meaning that instances of this type will not perform any custom
283287 /// test execution for tests they are applied to.
284- var customTestExecutor: CustomTestExecutor ? { get }
288+ var testExecutor: TestExecutor ? { get }
285289}
286290
287- extension Trait where Self : CustomTestExecuting {
291+ extension Trait where Self : TestExecuting {
288292 // Returns `self`.
289- public var customTestExecutor : Self ? { get }
293+ public var testExecutor : Self ? {
290294}
291295
292- extension Trait where CustomTestExecutor == Never {
296+ extension Trait where TestExecutor == Never {
293297 // Returns `nil`.
294- public var customTestExecutor: CustomTestExecutor ? { get }
298+ public var testExecutor: TestExecutor ? {
295299}
296300
297- extension Never : CustomTestExecuting {}
301+ extension Never : TestExecuting {}
298302```
299303
300304Here is a complete example of the usage scenario described earlier, showcasing
@@ -306,7 +310,7 @@ func example() {
306310 // ...validate API usage, referencing `APICredentials.current`...
307311}
308312
309- struct MockAPICredentialsTrait : TestTrait , CustomTestExecuting {
313+ struct MockAPICredentialsTrait : TestTrait , TestExecuting {
310314 func execute (_ function : @Sendable () async throws -> Void , for test : Test, testCase : Test.Case? ) async throws {
311315 let mockCredentials = APICredentials (apiKey : " ..." )
312316 try await APICredentials.$current.withValue (mockCredentials) {
@@ -357,26 +361,26 @@ concurrency technique and reduces the potential for test parallelization.
357361### Add `execute (... )` directly to the `Trait` protocol
358362
359363The proposed `execute (... )` method could be added as a requirement of the
360- ` Trait ` protocol instead of being part of a separate ` CustomTestExecuting `
361- protocol, and it could have a default implementation which directly invokes the
362- passed-in closure. But this approach would suffer from the lengthy backtrace
363- problem described above.
364+ `Trait` protocol instead of being part of a separate `TestExecuting` protocol,
365+ and it could have a default implementation which directly invokes the passed - in
366+ closure. But this approach would suffer from the lengthy backtrace problem
367+ described above.
364368
365369### Extend the `Trait` protocol
366370
367371The original, experimental implementation of this feature included a protocol
368372named`CustomExecutionTrait` which extended `Trait` and had roughly the same
369- method requirement as the ` CustomTestExecuting ` protocol proposed above. This
370- design worked, provided scoped access, and avoided the lengthy backtrace problem.
373+ method requirement as the `TestExecuting ` protocol proposed above. This design
374+ worked, provided scoped access, and avoided the lengthy backtrace problem.
371375
372376After evaluating the design and usage of this SPI though, it seemed unfortunate
373377to structure it as a sub- protocol of `Trait` because it means that the full
374378capabilities of the trait system are spread across multiple protocols. In the
375- proposed design, the ability to provide a custom test executor value is exposed
376- via the main ` Trait ` protocol, and it relies on an associated type to
377- conditionally opt-in to custom test behavior. In other words, the proposed
378- design expresses custom test behavior as just a _ capability_ that a trait may
379- have, rather than a distinct sub-type of trait.
379+ proposed design, the ability to provide a test executor value is exposed via the
380+ main `Trait` protocol, and it relies on an associated type to conditionally
381+ opt- in to custom test behavior. In other words, the proposed design expresses
382+ custom test behavior as just a _capability_ that a trait may have, rather than a
383+ distinct sub- type of trait.
380384
381385Also, the implementation of this approach within the testing library was not
382386ideal as it required a conditional `trait as? CustomExecutionTrait` downcast at
0 commit comments