Skip to content

Commit 3abf6f3

Browse files
Add a test trait for dependency key. (#308)
* Add a test trait for dependency key. * wip * wip * Update Sources/DependenciesTestSupport/TestTrait.swift * wip * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 96eecd4 commit 3abf6f3

File tree

4 files changed

+72
-4
lines changed

4 files changed

+72
-4
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ let package = Package(
5050
dependencies: [
5151
"Dependencies",
5252
"DependenciesMacros",
53+
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
5354
.product(name: "IssueReportingTestSupport", package: "xctest-dynamic-overlay"),
5455
]
5556
),

[email protected]

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ let package = Package(
5353
name: "DependenciesTestSupport",
5454
dependencies: [
5555
"Dependencies",
56+
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
5657
.product(name: "IssueReportingTestSupport", package: "xctest-dynamic-overlay"),
5758
]
5859
),

Sources/DependenciesTestSupport/TestTrait.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#if canImport(Testing)
1+
#if canImport(Testing) && compiler(>=6)
2+
import ConcurrencyExtras
23
import Dependencies
34
import Testing
45

@@ -40,11 +41,47 @@
4041
/// - Parameters:
4142
/// - keyPath: A key path to a dependency value.
4243
/// - value: A dependency value to override for the test.
43-
public static func dependency<Value: Sendable>(
44+
public static func dependency<Value>(
4445
_ keyPath: WritableKeyPath<DependencyValues, Value> & Sendable,
45-
_ value: Value
46+
_ value: sending Value
4647
) -> Self {
47-
Self { $0[keyPath: keyPath] = value }
48+
Self { [uncheckedValue = UncheckedSendable(value)] in
49+
$0[keyPath: keyPath] = uncheckedValue.wrappedValue
50+
}
51+
}
52+
53+
/// A trait that overrides a test's or suite's dependency.
54+
///
55+
/// Useful for overriding a dependency in a test without incurring the nesting and
56+
/// indentation of ``withDependencies(_:operation:)-4uz6m``.
57+
///
58+
/// ```swift
59+
/// struct Client: DependencyKey { … }
60+
/// @Test(
61+
/// .dependency(Client.mock)
62+
/// )
63+
/// func feature() {
64+
/// // ...
65+
/// }
66+
/// ```
67+
///
68+
/// > Important: Due to [a Swift bug](https://github.com/swiftlang/swift/issues/76409), it is
69+
/// > not possible to specify a closure directly inside a `@Suite` or `@Test` macro:
70+
/// >
71+
/// > ```swift
72+
/// > @Suite(
73+
/// > .dependency(Client { _ in .mock }) // 🛑
74+
/// > )
75+
/// > struct FeatureTests { /* ... */ }
76+
/// > ```
77+
///
78+
/// - Parameters:
79+
/// - keyPath: A key path to a dependency value.
80+
/// - value: A dependency value to override for the test.
81+
public static func dependency<Value: TestDependencyKey>(
82+
_ value: Value
83+
) -> Self where Value == Value.Value {
84+
Self { $0[Value.self] = value }
4885
}
4986

5087
/// A trait that overrides a test's or suite's dependencies.

Tests/DependenciesTests/SwiftTestingTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import ConcurrencyExtras
2+
13
#if canImport(Testing)
24
import Dependencies
35
import DependenciesTestSupport
@@ -78,6 +80,18 @@
7880
}
7981
}
8082
}
83+
84+
private static let mockClient = Client { 42 }
85+
@Test(.dependency(mockClient))
86+
func dependencyKeyTypeTrait() {
87+
@Dependency(Client.self) var client
88+
#expect(client.increment() == 42)
89+
}
90+
91+
@Test(.dependency(\.classClient, ClassClient()))
92+
func dependencyKeyNonSendableValue() {
93+
// NB: This test is to prove this trait compiles with a non-sendable type.
94+
}
8195
}
8296

8397
private struct Client: TestDependencyKey {
@@ -92,4 +106,19 @@
92106
}
93107
}
94108
}
109+
110+
class ClassClient {
111+
var count = 0
112+
}
113+
extension DependencyValues {
114+
var classClient: ClassClient {
115+
get { self[ClassClientKey.self].wrappedValue }
116+
set { self[ClassClientKey.self] = UncheckedSendable(newValue) }
117+
}
118+
}
119+
enum ClassClientKey: TestDependencyKey {
120+
static var testValue: UncheckedSendable<ClassClient> {
121+
UncheckedSendable(ClassClient())
122+
}
123+
}
95124
#endif

0 commit comments

Comments
 (0)