Skip to content

Commit 0ed16f2

Browse files
Allow tests to initialize non-stubbed dependencies
Add flag `allow-tests-to-init-real-dependencies` that will force all dependency resolver methods to be declared as `internal` rather than `private` or `fileprivate` so that any `@Testable import` of the generated code is able to initialize both the dependency container and access all of the DependencyResolvers. Note that this does **NOT** change any of the test mock generation, this is altering the access level of the **real** dependencies to be able to facilitate customization of test dependencies. It is then possible to mix and match real and mocked dependencies based on your test setup. Previously this was not possible because the MainDependencyContainer initializer was private, along with all of the dependency resolvers. Without setting this flag, there is no impact to code generation so this change is fully backward compatible. For example, now inside of unit tests for some `CoolSampleApp` , you would be able to do this: ``` @testable import CoolSampleApp class ExampleTests: XCTestCase { func testExample() { let realDependencyContainer = MainDependencyContainer() let realDependency = realDependencyContainer.specificDependencyResolver().specificDependency // Test code that actually uses the real dependency } } ``` It is still possible to use the `tests` flag to generate stubbed dependencies and use those alongside each other. For example, you can now mix and match real and mocked dependencies for a class under test to be able to do integration or feature level tests. Continuing the `textExample()` from above, you may see something like this: ``` func testExample() { let realDependencyContainer = MainDependencyContainer() let realDependencyFoo = realDependencyContainer.dependencyFooResolver().dependencyFoo let mockedDependencyContainer = StubbedDependencies() let mockedDependencyBar = StubbedDependencies.dependencyBarDouble let objectUnderTest = SomeClass(foo: realDependencyFoo, bar: mockedDependencyBar) // Actual test logic and validation } ```
1 parent 70d6eab commit 0ed16f2

File tree

8 files changed

+59
-48
lines changed

8 files changed

+59
-48
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ xcuserdata/
2121
*.moved-aside
2222
*.xccheckout
2323
*.xcscmblueprint
24+
*DS_Store
2425

2526
## Obj-C/Swift specific
2627
*.hmap

.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.6
1+
1.1.7

Package.resolved

+15-33
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
{
22
"object": {
33
"pins": [
4-
{
5-
"package": "Commandant",
6-
"repositoryURL": "https://github.com/Carthage/Commandant.git",
7-
"state": {
8-
"branch": null,
9-
"revision": "ab68611013dec67413628ac87c1f29e8427bc8e4",
10-
"version": "0.17.0"
11-
}
12-
},
134
{
145
"package": "Commander",
156
"repositoryURL": "https://github.com/kylef/Commander.git",
@@ -28,15 +19,6 @@
2819
"version": null
2920
}
3021
},
31-
{
32-
"package": "Nimble",
33-
"repositoryURL": "https://github.com/Quick/Nimble.git",
34-
"state": {
35-
"branch": null,
36-
"revision": "7a46a5fc86cb917f69e3daf79fcb045283d8f008",
37-
"version": "8.1.2"
38-
}
39-
},
4022
{
4123
"package": "PathKit",
4224
"repositoryURL": "https://github.com/kylef/PathKit.git",
@@ -46,15 +28,6 @@
4628
"version": "0.9.2"
4729
}
4830
},
49-
{
50-
"package": "Quick",
51-
"repositoryURL": "https://github.com/Quick/Quick.git",
52-
"state": {
53-
"branch": null,
54-
"revision": "09b3becb37cb2163919a3842a4c5fa6ec7130792",
55-
"version": "2.2.1"
56-
}
57-
},
5831
{
5932
"package": "Rainbow",
6033
"repositoryURL": "https://github.com/onevcat/Rainbow",
@@ -78,8 +51,8 @@
7851
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
7952
"state": {
8053
"branch": null,
81-
"revision": "77a4dbbb477a8110eb8765e3c44c70fb4929098f",
82-
"version": "0.29.0"
54+
"revision": "eb6656ed26bdef967ad8d07c27e2eab34dc582f2",
55+
"version": "0.37.0"
8356
}
8457
},
8558
{
@@ -91,22 +64,31 @@
9164
"version": "0.9.2"
9265
}
9366
},
67+
{
68+
"package": "swift-argument-parser",
69+
"repositoryURL": "https://github.com/apple/swift-argument-parser.git",
70+
"state": {
71+
"branch": null,
72+
"revision": "41982a3656a71c768319979febd796c6fd111d5c",
73+
"version": "1.5.0"
74+
}
75+
},
9476
{
9577
"package": "SWXMLHash",
9678
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
9779
"state": {
9880
"branch": null,
99-
"revision": "9183170d20857753d4f331b0ca63f73c60764bf3",
100-
"version": "5.0.2"
81+
"revision": "a853604c9e9a83ad9954c7e3d2a565273982471f",
82+
"version": "7.0.2"
10183
}
10284
},
10385
{
10486
"package": "Yams",
10587
"repositoryURL": "https://github.com/jpsim/Yams.git",
10688
"state": {
10789
"branch": null,
108-
"revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f",
109-
"version": "2.0.0"
90+
"revision": "b4b8042411dc7bbb696300a34a4bf3ba1b7ad19b",
91+
"version": "5.3.1"
11092
}
11193
}
11294
]

Package.swift

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
1-
// swift-tools-version:5.0
1+
// swift-tools-version:5.5
22
import PackageDescription
33

44
let package = Package(
55
name: "Weaver",
66
platforms: [
7-
.macOS(.v10_13)
7+
.macOS(.v12)
88
],
99
products: [
10-
.executable(name: "Weaver", targets: ["WeaverMain"])
10+
.executable(name: "Weaver", targets: ["WeaverMain"])
1111
],
1212
dependencies: [
13-
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.27.0"),
13+
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.37.0"),
1414
.package(url: "https://github.com/kylef/Commander.git", from: "0.9.0"),
1515
.package(url: "https://github.com/onevcat/Rainbow", from: "3.0.0"),
16-
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"),
16+
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.0"),
1717
.package(url: "https://github.com/kylef/PathKit.git", from: "0.9.0"),
1818
.package(url: "https://github.com/scribd/Meta.git", .branch("master")),
1919
.package(url: "https://github.com/JohnSundell/ShellOut.git", from: "2.2.0")
2020
],
2121
targets: [
22-
.target(name: "WeaverCodeGen", dependencies: ["SourceKittenFramework", "Meta", "PathKit"]),
22+
.target(name: "WeaverCodeGen", dependencies: [
23+
.product(name: "SourceKittenFramework", package: "SourceKitten"),
24+
"Meta",
25+
"PathKit"
26+
]),
2327
.testTarget(name: "WeaverCodeGenTests", dependencies: ["WeaverCodeGen"]),
2428
.target(name: "WeaverCommand", dependencies: ["PathKit", "Commander", "Rainbow", "Yams", "WeaverCodeGen", "ShellOut"]),
2529
.testTarget(name: "WeaverCommandTests", dependencies: ["WeaverCommand"]),
26-
.target(name: "WeaverMain", dependencies: ["WeaverCommand"])
30+
.executableTarget(name: "WeaverMain", dependencies: ["WeaverCommand"])
2731
]
2832
)

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ Options:
549549
--ignored-path - Paths to ignore.
550550
--cache-path - Where the cache gets stored.
551551
--recursive-off
552+
--allow-tests-to-init-real-dependencies - Makes dependency resolvers available to @Testable imports.
552553
--tests - Activates the test helpers' generation.
553554
--testable-imports - Modules to imports in the test helpers.
554555
--swiftlint-disable-all - Disables all swiftlint rules.
@@ -578,6 +579,7 @@ weaver swift --project-path $PROJECT_DIR/$PROJECT_NAME --main-output-path Genera
578579
- `--platform` - Platform for which the generated code will be compiled (iOS, watchOS, OSX, macOS or tvOS).
579580
- `--included-imports` - Modules which can be imported in generated files.
580581
- `--excluded-imports` - Modules which can't be imported in generated files.
582+
- `--allow_tests_to_init_real_dependencies` - Will declare every DependencyResolver with internal access level so `@Testable import`s can invoke them to initialize real dependencies in tests.
581583

582584
### Configuration File:
583585

Sources/WeaverCodeGen/SwiftGenerator.swift

+14-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public final class SwiftGenerator {
2828

2929
private let mainActor: Bool
3030

31+
private let allowTestsToInitRealDependencies: Bool
32+
3133
public typealias ImportFilter = (String) -> Bool
3234
private let importFilter: ImportFilter
3335

@@ -39,6 +41,7 @@ public final class SwiftGenerator {
3941
testableImports: [String]?,
4042
swiftlintDisableAll: Bool,
4143
mainActor: Bool,
44+
allowTestsToInitRealDependencies: Bool,
4245
importFilter: @escaping ImportFilter) throws {
4346

4447
self.dependencyGraph = dependencyGraph
@@ -49,6 +52,7 @@ public final class SwiftGenerator {
4952
self.testableImports = testableImports?.filter(importFilter)
5053
self.swiftlintDisableAll = swiftlintDisableAll
5154
self.mainActor = mainActor
55+
self.allowTestsToInitRealDependencies = allowTestsToInitRealDependencies
5256
self.importFilter = importFilter
5357
}
5458

@@ -77,6 +81,7 @@ public final class SwiftGenerator {
7781
version,
7882
testableImports,
7983
swiftlintDisableAll,
84+
allowTestsToInitRealDependencies,
8085
importFilter
8186
)
8287
_file = file
@@ -216,6 +221,8 @@ private final class MetaWeaverFile {
216221

217222
private let mainActor: Bool
218223

224+
private let allowTestsToInitRealDependencies: Bool
225+
219226
private let importFilter: SwiftGenerator.ImportFilter
220227

221228
// Pre computed data
@@ -244,6 +251,7 @@ private final class MetaWeaverFile {
244251
_ version: String,
245252
_ testableImports: [String]?,
246253
_ swiftlintDisableAll: Bool,
254+
_ allowTestsToInitRealDependencies: Bool,
247255
_ importFilter: @escaping SwiftGenerator.ImportFilter) throws {
248256

249257
self.dependencyGraph = dependencyGraph
@@ -253,6 +261,7 @@ private final class MetaWeaverFile {
253261
self.version = version
254262
self.testableImports = testableImports
255263
self.swiftlintDisableAll = swiftlintDisableAll
264+
self.allowTestsToInitRealDependencies = allowTestsToInitRealDependencies
256265
self.importFilter = importFilter
257266

258267
let desambiguatedDeclarations = try MetaWeaverFile.desambiguatedDeclarations(from: dependencyGraph)
@@ -367,18 +376,17 @@ private extension MetaWeaverFile {
367376
.adding(member: Property(variable: Variable(name: "provider").with(immutable: true).with(type: .provider)).with(accessLevel: .private))
368377
.adding(member: EmptyLine())
369378

370-
379+
let accessLevel: Meta.AccessLevel = self.allowTestsToInitRealDependencies ? .internal : .fileprivate
371380
if self.mainActor == true {
372-
373381
type = type.adding(member: PlainCode(code: """
374-
fileprivate init(provider: Provider? = nil) {
382+
\(accessLevel.swiftString) init(provider: Provider? = nil) {
375383
self.provider = provider ?? Provider()
376384
}
377385
"""))
378386

379387
} else {
380388
type = type.adding(member: Function(kind: .`init`(convenience: false, optional: false))
381-
.with(accessLevel: .fileprivate)
389+
.with(accessLevel: accessLevel)
382390
.adding(parameter: FunctionParameter(name: "provider", type: .provider).with(defaultValue: TypeIdentifier.provider.reference | .call()))
383391
.adding(members: [
384392
Assignment(variable: Reference.named(.`self`) + .named("provider"), value: Reference.named("provider")),
@@ -883,7 +891,7 @@ static func _pushDynamicResolver<Resolver>(_ resolver: Resolver) {
883891
EmptyLine(),
884892
Function(kind: .named(publicInterface ?
885893
dependencyContainer.type.publicDependencyResolverVariable.name : dependencyContainer.type.dependencyResolverVariable.name))
886-
.with(accessLevel: accessLevel)
894+
.with(accessLevel: self.allowTestsToInitRealDependencies ? .internal : accessLevel)
887895
.with(resultType: containsAmbiguousDeclarations ?
888896
dependencyContainer.type.dependencyResolverProxyTypeID : dependencyContainer.type.dependencyResolverTypeID
889897
)
@@ -1456,7 +1464,7 @@ private extension MetaWeaverFile {
14561464
14571465
// MARK: - Provider
14581466
1459-
private final class Provider {
1467+
\(self.allowTestsToInitRealDependencies ? "internal" : "private") final class Provider {
14601468
14611469
typealias ParametersCopier = (Provider) -> Void
14621470
typealias Builder<T> = (ParametersCopier?) -> T

Sources/WeaverCommand/Command.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Darwin
1313
import PathKit
1414
import Rainbow
1515

16-
private let version = "1.1.6"
16+
private let version = "1.1.7"
1717

1818
// MARK: - Linker
1919

@@ -115,6 +115,7 @@ private enum Parameters {
115115
static let recursiveOff = OptionalFlag("recursive-off", disabledName: "recursive-on")
116116
static let pretty = Flag("pretty", default: false)
117117
static let mainActor = Flag("mainactor", default: false)
118+
static let allowTestsToInitRealDependencies = OptionalFlag("allow-tests-to-init-real-dependencies", default: nil)
118119
static let tests = OptionalFlag("tests", default: nil)
119120
static let testableImports = VariadicOption<String>("testable-imports", default: [], description: "Modules to import for testing.")
120121
static let swiftlintDisableAll = OptionalFlag("swiftlint-disable-all", default: nil)
@@ -139,6 +140,7 @@ public let weaverCommand = Group {
139140
Parameters.cachePath,
140141
Parameters.recursiveOff,
141142
Parameters.mainActor,
143+
Parameters.allowTestsToInitRealDependencies,
142144
Parameters.tests,
143145
Parameters.testableImports,
144146
Parameters.swiftlintDisableAll,
@@ -156,6 +158,7 @@ public let weaverCommand = Group {
156158
cachePath,
157159
recursiveOff,
158160
mainActor,
161+
allowTestsToInitRealDependencies,
159162
tests,
160163
testableImports,
161164
swiftlintDisableAll,
@@ -174,6 +177,7 @@ public let weaverCommand = Group {
174177
cachePath: cachePath,
175178
recursiveOff: recursiveOff,
176179
tests: tests,
180+
allowTestsToInitRealDependencies: allowTestsToInitRealDependencies,
177181
testableImports: testableImports.isEmpty ? nil : testableImports,
178182
swiftlintDisableAll: swiftlintDisableAll,
179183
platform: Platform(platform),
@@ -235,6 +239,7 @@ public let weaverCommand = Group {
235239
testableImports: configuration.testableImports,
236240
swiftlintDisableAll: configuration.swiftlintDisableAll,
237241
mainActor: mainActor,
242+
allowTestsToInitRealDependencies: configuration.allowTestsToInitRealDependencies,
238243
importFilter: configuration.importFilter
239244
)
240245

Sources/WeaverCommand/Configuration.swift

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct Configuration {
2424
let cachePath: Path
2525
let recursiveOff: Bool
2626
let tests: Bool
27+
let allowTestsToInitRealDependencies: Bool
2728
let testableImports: [String]?
2829
let swiftlintDisableAll: Bool
2930
let platform: Platform?
@@ -39,6 +40,7 @@ struct Configuration {
3940
cachePath: Path?,
4041
recursiveOff: Bool?,
4142
tests: Bool?,
43+
allowTestsToInitRealDependencies: Bool?,
4244
testableImports: [String]?,
4345
swiftlintDisableAll: Bool?,
4446
platform: Platform?,
@@ -57,6 +59,7 @@ struct Configuration {
5759
self.cachePath = cachePath ?? Defaults.cachePath
5860
self.recursiveOff = recursiveOff ?? Defaults.recursiveOff
5961
self.tests = tests ?? Defaults.tests
62+
self.allowTestsToInitRealDependencies = allowTestsToInitRealDependencies ?? Defaults.allowTestsToInitRealDependencies
6063
self.testableImports = testableImports
6164
self.swiftlintDisableAll = swiftlintDisableAll ?? Defaults.swiftlintDisableAll
6265
self.platform = platform
@@ -74,6 +77,7 @@ struct Configuration {
7477
cachePath: Path? = nil,
7578
recursiveOff: Bool? = nil,
7679
tests: Bool? = nil,
80+
allowTestsToInitRealDependencies: Bool? = nil,
7781
testableImports: [String]? = nil,
7882
swiftlintDisableAll: Bool? = nil,
7983
platform: Platform? = nil,
@@ -103,6 +107,7 @@ struct Configuration {
103107
cachePath: _cachePath,
104108
recursiveOff: recursiveOff,
105109
tests: tests,
110+
allowTestsToInitRealDependencies: allowTestsToInitRealDependencies,
106111
testableImports: testableImports,
107112
swiftlintDisableAll: swiftlintDisableAll,
108113
platform: platform,
@@ -127,6 +132,7 @@ struct Configuration {
127132
self.cachePath = cachePath ?? configuration.cachePath
128133
self.recursiveOff = recursiveOff ?? configuration.recursiveOff
129134
self.tests = tests ?? configuration.tests
135+
self.allowTestsToInitRealDependencies = allowTestsToInitRealDependencies ?? configuration.allowTestsToInitRealDependencies
130136
self.testableImports = testableImports ?? configuration.testableImports
131137
self.swiftlintDisableAll = swiftlintDisableAll ?? configuration.swiftlintDisableAll
132138
self.platform = platform ?? configuration.platform
@@ -172,6 +178,7 @@ extension Configuration {
172178
static let inputPathStrings = ["."]
173179
static let detailedResolvers = false
174180
static let tests = false
181+
static let allowTestsToInitRealDependencies = false
175182
static let swiftlintDisableAll = true
176183

177184
static var projectPath: Path {
@@ -197,6 +204,7 @@ extension Configuration: Decodable {
197204
case ignoredPaths = "ignored_paths"
198205
case recursive
199206
case tests
207+
case allowTestsToInitRealDependencies = "allow_tests_to_init_real_dependencies"
200208
case testableImports = "testable_imports"
201209
case cachePath = "cache_path"
202210
case swiftlintDisableAll = "swiftlint_disable_all"
@@ -224,6 +232,7 @@ extension Configuration: Decodable {
224232
cachePath: try container.decodeIfPresent(Path.self, forKey: .cachePath),
225233
recursiveOff: recursive.map { !$0 },
226234
tests: try container.decodeIfPresent(Bool.self, forKey: .tests),
235+
allowTestsToInitRealDependencies: try container.decodeIfPresent(Bool.self, forKey: .allowTestsToInitRealDependencies),
227236
testableImports: try container.decodeIfPresent([String].self, forKey: .testableImports),
228237
swiftlintDisableAll: try container.decodeIfPresent(Bool.self, forKey: .swiftlintDisableAll),
229238
platform: try container.decodeIfPresent(Platform.self, forKey: .platform),

0 commit comments

Comments
 (0)