Skip to content

Commit

Permalink
Add a test trait for dependency key. (#308)
Browse files Browse the repository at this point in the history
* Add a test trait for dependency key.

* wip

* wip

* Update Sources/DependenciesTestSupport/TestTrait.swift

* wip

* wip

---------

Co-authored-by: Stephen Celis <stephen@stephencelis.com>
  • Loading branch information
mbrandonw and stephencelis authored Nov 15, 2024
1 parent 96eecd4 commit 3abf6f3
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 4 deletions.
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ let package = Package(
dependencies: [
"Dependencies",
"DependenciesMacros",
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "IssueReportingTestSupport", package: "xctest-dynamic-overlay"),
]
),
Expand Down
1 change: 1 addition & 0 deletions Package@swift-6.0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ let package = Package(
name: "DependenciesTestSupport",
dependencies: [
"Dependencies",
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "IssueReportingTestSupport", package: "xctest-dynamic-overlay"),
]
),
Expand Down
45 changes: 41 additions & 4 deletions Sources/DependenciesTestSupport/TestTrait.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#if canImport(Testing)
#if canImport(Testing) && compiler(>=6)
import ConcurrencyExtras
import Dependencies
import Testing

Expand Down Expand Up @@ -40,11 +41,47 @@
/// - Parameters:
/// - keyPath: A key path to a dependency value.
/// - value: A dependency value to override for the test.
public static func dependency<Value: Sendable>(
public static func dependency<Value>(
_ keyPath: WritableKeyPath<DependencyValues, Value> & Sendable,
_ value: Value
_ value: sending Value
) -> Self {
Self { $0[keyPath: keyPath] = value }
Self { [uncheckedValue = UncheckedSendable(value)] in
$0[keyPath: keyPath] = uncheckedValue.wrappedValue
}
}

/// A trait that overrides a test's or suite's dependency.
///
/// Useful for overriding a dependency in a test without incurring the nesting and
/// indentation of ``withDependencies(_:operation:)-4uz6m``.
///
/// ```swift
/// struct Client: DependencyKey { … }
/// @Test(
/// .dependency(Client.mock)
/// )
/// func feature() {
/// // ...
/// }
/// ```
///
/// > Important: Due to [a Swift bug](https://github.com/swiftlang/swift/issues/76409), it is
/// > not possible to specify a closure directly inside a `@Suite` or `@Test` macro:
/// >
/// > ```swift
/// > @Suite(
/// > .dependency(Client { _ in .mock }) // 🛑
/// > )
/// > struct FeatureTests { /* ... */ }
/// > ```
///
/// - Parameters:
/// - keyPath: A key path to a dependency value.
/// - value: A dependency value to override for the test.
public static func dependency<Value: TestDependencyKey>(
_ value: Value
) -> Self where Value == Value.Value {
Self { $0[Value.self] = value }
}

/// A trait that overrides a test's or suite's dependencies.
Expand Down
29 changes: 29 additions & 0 deletions Tests/DependenciesTests/SwiftTestingTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import ConcurrencyExtras

#if canImport(Testing)
import Dependencies
import DependenciesTestSupport
Expand Down Expand Up @@ -78,6 +80,18 @@
}
}
}

private static let mockClient = Client { 42 }
@Test(.dependency(mockClient))
func dependencyKeyTypeTrait() {
@Dependency(Client.self) var client
#expect(client.increment() == 42)
}

@Test(.dependency(\.classClient, ClassClient()))
func dependencyKeyNonSendableValue() {
// NB: This test is to prove this trait compiles with a non-sendable type.
}
}

private struct Client: TestDependencyKey {
Expand All @@ -92,4 +106,19 @@
}
}
}

class ClassClient {
var count = 0
}
extension DependencyValues {
var classClient: ClassClient {
get { self[ClassClientKey.self].wrappedValue }
set { self[ClassClientKey.self] = UncheckedSendable(newValue) }
}
}
enum ClassClientKey: TestDependencyKey {
static var testValue: UncheckedSendable<ClassClient> {
UncheckedSendable(ClassClient())
}
}
#endif

0 comments on commit 3abf6f3

Please sign in to comment.