diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fc24e7..cf90114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ __Sections__ - Support for multiple arguments. Code clean up. - Using parameter packs for multiple arguments +- Tests are converted to the new Swift Testing framework ## [1.0.4] diff --git a/README.md b/README.md index 3326919..e0ec71c 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ container.register { container in In the factory closure, we typically just call the dependency initializer and we resolve its input parameters from the container. You can get rid of this duplicated boiler-plate by using `autoregister` method where you specify just the initializer that should be used to initialize the dependency, instead of writing the same factories over and over again. The above example then looks like this: ```swift let container = Container() -container.autoregister(initializer: Dependency.init) +container.autoregister(in: .shared, initializer: Dependency.init) ``` Similarly, we can use autoregistration with an argument and replace this: ```swift @@ -166,7 +166,7 @@ class Singletons { static let container = Container() static func configure() { - container.autoregister(initializer: Dependency.init) + container.autoregister(in: .shared, initializer: Dependency.init) } } @@ -183,7 +183,7 @@ class Object { When using the `@Injected` property wrapper, the dependency is resolved right in the moment when the property is instantiated. If you prefer to resolve the dependency only when it is accessed for the first time, you should rather use `@LazyInjected`: ```swift let container = Container() -container.autoregister(initializer: Dependency.init) +container.autoregister(in: .shared, initializer: Dependency.init) class Object { @LazyInjected(from: container) var dependency: Dependency diff --git a/Sources/Container/Async/AsyncContainer.swift b/Sources/Container/Async/AsyncContainer.swift index abd6b90..1716f7f 100644 --- a/Sources/Container/Async/AsyncContainer.swift +++ b/Sources/Container/Async/AsyncContainer.swift @@ -99,6 +99,12 @@ public actor AsyncContainer: AsyncDependencyResolving, AsyncDependencyRegisterin public func tryResolve(type: Dependency.Type, arguments: repeat each Argument) async throws -> Dependency { let identifier = RegistrationIdentifier(type: type, argumentTypes: repeat (each Argument).self) + if identifier.argumentCount > RegistrationIdentifierConstant.maximumArgumentCount { + throw ResolutionError.tooManyArguments( + message: "Maximum number of arguments is \(RegistrationIdentifierConstant.maximumArgumentCount). Got \(identifier.argumentCount)." + ) + } + let registration = try getRegistration(with: identifier) // Pack arguments into a tuple for storage - this matches how AsyncRegistration expects them diff --git a/Sources/Container/Sync/Container.swift b/Sources/Container/Sync/Container.swift index 59d62d4..49f854b 100644 --- a/Sources/Container/Sync/Container.swift +++ b/Sources/Container/Sync/Container.swift @@ -95,6 +95,12 @@ open class Container: DependencyAutoregistering, DependencyResolving, Dependency open func tryResolve(type: Dependency.Type, arguments: repeat each Argument) throws -> Dependency { let identifier = RegistrationIdentifier(type: type, argumentTypes: repeat (each Argument).self) + if identifier.argumentCount > RegistrationIdentifierConstant.maximumArgumentCount { + throw ResolutionError.tooManyArguments( + message: "Maximum number of arguments is \(RegistrationIdentifierConstant.maximumArgumentCount). Got \(identifier.argumentCount)." + ) + } + let registration = try getRegistration(with: identifier) // Pack arguments into a tuple for storage - this matches how Registration expects them diff --git a/Sources/Models/RegistrationIdentifier.swift b/Sources/Models/RegistrationIdentifier.swift index 8ad361b..0a0f08d 100644 --- a/Sources/Models/RegistrationIdentifier.swift +++ b/Sources/Models/RegistrationIdentifier.swift @@ -7,8 +7,8 @@ import Foundation -/// Maximum number of arguments supported for dependency resolution -private enum Constant { +/// Maximum number of arguments supported for dependency resolution (enforced at resolve time) +enum RegistrationIdentifierConstant { static let maximumArgumentCount = 3 } @@ -17,7 +17,11 @@ struct RegistrationIdentifier: Sendable { let typeIdentifier: ObjectIdentifier let argumentIdentifiers: [ObjectIdentifier] - /// Initializer using parameter packs for any number of argument types + /// Number of argument types (used to enforce maximum at resolve time) + var argumentCount: Int { argumentIdentifiers.count } + + /// Initializer using parameter packs for any number of argument types. + /// Registration with more than 3 arguments is allowed; resolution with more than 3 arguments will throw. /// /// - Parameters: /// - type: Type of the dependency @@ -28,11 +32,6 @@ struct RegistrationIdentifier: Sendable { var identifiers: [ObjectIdentifier] = [] repeat identifiers.append(ObjectIdentifier((each Argument).self)) - precondition( - identifiers.count <= Constant.maximumArgumentCount, - "Maximum number of arguments is \(Constant.maximumArgumentCount). Got \(identifiers.count)." - ) - argumentIdentifiers = identifiers } diff --git a/Sources/Models/ResolutionError.swift b/Sources/Models/ResolutionError.swift index 3717c4f..27eda3a 100644 --- a/Sources/Models/ResolutionError.swift +++ b/Sources/Models/ResolutionError.swift @@ -15,12 +15,17 @@ public enum ResolutionError: Error { /// The dependency with the required type is registered within the container but the factory closure expects a different argument type case unmatchingArgumentType(message: String) + /// More than the maximum supported number of arguments (3) was passed to resolve + case tooManyArguments(message: String) + public var localizedDescription: String { switch self { case let .dependencyNotRegistered(message): return "Dependency not registered: \(message)" case let .unmatchingArgumentType(message): return "Unmatching argument type: \(message)" + case let .tooManyArguments(message): + return "Too many arguments: \(message)" } } } diff --git a/Sources/Protocols/Registration/Async/AsyncDependencyRegistering.swift b/Sources/Protocols/Registration/Async/AsyncDependencyRegistering.swift index 4c8282b..f1e1247 100644 --- a/Sources/Protocols/Registration/Async/AsyncDependencyRegistering.swift +++ b/Sources/Protocols/Registration/Async/AsyncDependencyRegistering.swift @@ -38,13 +38,6 @@ public protocol AsyncDependencyRegistering { // MARK: Overloaded factory methods public extension AsyncDependencyRegistering { - /// Default ``DependencyScope`` value - /// - /// The default value is `shared` - static var defaultScope: DependencyScope { - DependencyScope.shared - } - /// Register a dependency with an implicit type determined by the factory closure return type /// /// - Parameters: diff --git a/Sources/Protocols/Registration/Sync/DependencyAutoregistering.swift b/Sources/Protocols/Registration/Sync/DependencyAutoregistering.swift index 7ce3339..ef0bc09 100644 --- a/Sources/Protocols/Registration/Sync/DependencyAutoregistering.swift +++ b/Sources/Protocols/Registration/Sync/DependencyAutoregistering.swift @@ -148,11 +148,11 @@ public extension DependencyAutoregistering { /// /// - Parameters: /// - type: Type of the dependency to register. Default value is implicitly inferred from the initializer return type - /// - scope: Scope of the dependency. If `.new` is used, the initializer is called on each `resolve` call. If `.shared` is used, the initializer is called only the first time, the instance is cached and it is returned for all subsequent `resolve` calls, i.e. it is a singleton. The default value is `.shared` + /// - scope: Scope of the dependency. If `.new` is used, the initializer is called on each `resolve` call. If `.shared` is used, the initializer is called only the first time, the instance is cached and it is returned for all subsequent `resolve` calls, i.e. it is a singleton /// - initializer: Initializer method of the `Dependency` that should be used to instantiate the dependency when it is being resolved from the container func autoregister( type: Dependency.Type = Dependency.self, - in scope: DependencyScope = Self.defaultScope, + in scope: DependencyScope, initializer: @escaping (repeat each Parameter) -> Dependency ) { let factory: Factory = { resolver in diff --git a/Sources/Protocols/Registration/Sync/DependencyRegistering.swift b/Sources/Protocols/Registration/Sync/DependencyRegistering.swift index 7c4f932..d920326 100644 --- a/Sources/Protocols/Registration/Sync/DependencyRegistering.swift +++ b/Sources/Protocols/Registration/Sync/DependencyRegistering.swift @@ -38,13 +38,6 @@ public protocol DependencyRegistering { // MARK: Overloaded factory methods public extension DependencyRegistering { - /// Default ``DependencyScope`` value - /// - /// The default value is `shared` - static var defaultScope: DependencyScope { - DependencyScope.shared - } - /// Register a dependency with an implicit type determined by the factory closure return type /// /// - Parameters: diff --git a/Tests/Common/Async/AsyncDITestCase.swift b/Tests/Common/Async/AsyncDITestCase.swift deleted file mode 100644 index 76a06fc..0000000 --- a/Tests/Common/Async/AsyncDITestCase.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// AsyncDITestCase.swift -// DependencyInjection -// -// Created by RĂ³bert Oravec on 19.12.2024. -// - -import DependencyInjection -import XCTest - -class AsyncDITestCase: XCTestCase { - var container: AsyncContainer! - - override func setUp() { - super.setUp() - - container = AsyncContainer() - } - - override func tearDown() { - container = nil - - super.tearDown() - } -} diff --git a/Tests/Common/Dependencies.swift b/Tests/Common/Dependencies.swift index 4260ca4..f760fcf 100644 --- a/Tests/Common/Dependencies.swift +++ b/Tests/Common/Dependencies.swift @@ -130,6 +130,21 @@ final class DependencyWithThreeArguments: Sendable { } } +/// Used only in tests to verify that more than 3 arguments throws ``ResolutionError/tooManyArguments(message:)`` +final class DependencyWithFourArguments: Sendable { + let argument1: StructureDependency + let argument2: String + let argument3: Int + let argument4: Bool + + init(argument1: StructureDependency, argument2: String, argument3: Int, argument4: Bool) { + self.argument1 = argument1 + self.argument2 = argument2 + self.argument3 = argument3 + self.argument4 = argument4 + } +} + final class DependencyWithAsyncInitWithTwoArguments: Sendable { let argument1: StructureDependency let argument2: String diff --git a/Tests/Common/Sync/DITestCase.swift b/Tests/Common/Sync/DITestCase.swift deleted file mode 100644 index 5bcded5..0000000 --- a/Tests/Common/Sync/DITestCase.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// DITestCase.swift -// -// -// Created by Jan Schwarz on 04.01.2022. -// - -import DependencyInjection -import XCTest - -class DITestCase: XCTestCase { - var container: Container! - - override func setUp() { - super.setUp() - - container = Container() - } - - override func tearDown() { - container = nil - - super.tearDown() - } -} diff --git a/Tests/Common/TestTags.swift b/Tests/Common/TestTags.swift new file mode 100644 index 0000000..70e8e69 --- /dev/null +++ b/Tests/Common/TestTags.swift @@ -0,0 +1,18 @@ +// +// TestTags.swift +// DependencyInjection +// +// Created on 19.12.2024. +// + +import Testing + +extension Tag { + @Tag static var async: Tag + @Tag static var sync: Tag + @Tag static var base: Tag + @Tag static var arguments: Tag + @Tag static var complex: Tag + @Tag static var autoregistration: Tag + @Tag static var propertyWrappers: Tag +} diff --git a/Tests/Container/Async/AsyncArgumentTests.swift b/Tests/Container/Async/AsyncArgumentTests.swift index 5166d34..759a340 100644 --- a/Tests/Container/Async/AsyncArgumentTests.swift +++ b/Tests/Container/Async/AsyncArgumentTests.swift @@ -6,230 +6,326 @@ // import DependencyInjection -import XCTest - -final class AsyncContainerArgumentTests: AsyncDITestCase { - func testRegistration() async { - await container.register { _, argument -> DependencyWithValueTypeParameter in +import Testing + +@Suite("Container/Async/Arguments", .tags(.async, .arguments)) +struct AsyncContainerArgumentTests { + @Test("Registration with single argument") + func registration() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument -> DependencyWithValueTypeParameter in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithValueTypeParameter = await container.resolve(arguments: argument) - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") + // When + let resolvedDependency: DependencyWithValueTypeParameter = await subject.resolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + + // Then + #expect(argument == resolvedDependency.subDependency) } - func testRegistrationWithExplicitType() async { - await container.register(type: DependencyWithValueTypeParameter.self) { _, argument in + @Test("Registration with explicit type") + func registrationWithExplicitType() async { + // Given + let subject = AsyncContainer() + await subject.register(type: DependencyWithValueTypeParameter.self) { _, argument in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithValueTypeParameter = await container.resolve(arguments: argument) - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") + // When + let resolvedDependency: DependencyWithValueTypeParameter = await subject.resolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + + // Then + #expect(argument == resolvedDependency.subDependency) } - func testUnmatchingArgumentType_ZeroArguments() async { - await container.register(in: .shared) { _ -> SimpleDependency in + @Test("Unmatching argument type - zero arguments") + func unmatchingArgumentType_ZeroArguments() async throws { + // Given + let subject = AsyncContainer() + await subject.register { _ -> SimpleDependency in SimpleDependency() } - let argument = 48 + // When do { - _ = try await container.tryResolve(type: SimpleDependency.self, arguments: argument) - - XCTFail("Expected to throw error") + _ = try await subject.tryResolve(type: SimpleDependency.self, arguments: argument) + Issue.record("Expected to throw error") } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testUnmatchingArgumentType_OneArgument() async { - await container.register { _, argument -> DependencyWithValueTypeParameter in + @Test("Unmatching argument type - one argument") + func unmatchingArgumentType_OneArgument() async throws { + // Given + let subject = AsyncContainer() + await subject.register { _, argument -> DependencyWithValueTypeParameter in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = 48 + // When do { - _ = try await container.tryResolve(type: DependencyWithValueTypeParameter.self, arguments: argument) - - XCTFail("Expected to throw error") + _ = try await subject.tryResolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + Issue.record("Expected to throw error") } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testRegistrationWithAsyncInit() async { - await container.register { _, argument -> DependencyWithAsyncInitWithParameter in - await DependencyWithAsyncInitWithParameter(subDependency: argument) - } - - let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithAsyncInitWithParameter = await container.resolve(arguments: argument) - - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") - } - - func testRegistrationWithTwoArguments() async { - await container.register { _, argument1, argument2 -> DependencyWithTwoArguments in + @Test("Registration with two arguments") + func registrationWithTwoArguments() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2 -> DependencyWithTwoArguments in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" - let resolvedDependency: DependencyWithTwoArguments = await container.resolve(arguments: argument1, argument2) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") + // When + let resolvedDependency: DependencyWithTwoArguments = await subject.resolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) } - func testRegistrationWithTwoArgumentsWithExplicitType() async { - await container.register(type: DependencyWithTwoArguments.self) { _, argument1, argument2 in + @Test("Registration with two arguments with explicit type") + func registrationWithTwoArgumentsWithExplicitType() async { + // Given + let subject = AsyncContainer() + await subject.register(type: DependencyWithTwoArguments.self) { _, argument1, argument2 in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" - let resolvedDependency: DependencyWithTwoArguments = await container.resolve(arguments: argument1, argument2) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") + // When + let resolvedDependency: DependencyWithTwoArguments = await subject.resolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) } - func testUnmatchingArgumentType_TwoArguments() async { - await container.register { _, argument1, argument2 -> DependencyWithTwoArguments in + @Test("Unmatching argument type - two arguments") + func unmatchingArgumentType_TwoArguments() async throws { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2 -> DependencyWithTwoArguments in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = 48 let argument2 = "test" + // When do { - _ = try await container.tryResolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) - - XCTFail("Expected to throw error") + _ = try await subject.tryResolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + Issue.record("Expected to throw error") } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testRegistrationWithThreeArguments() async { - await container.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in + @Test("Registration with three arguments") + func registrationWithThreeArguments() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" let argument3 = 42 - let resolvedDependency: DependencyWithThreeArguments = await container.resolve(arguments: argument1, argument2, argument3) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") - XCTAssertEqual(argument3, resolvedDependency.argument3, "Container returned dependency with different third argument") + // When + let resolvedDependency: DependencyWithThreeArguments = await subject.resolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) + #expect(argument3 == resolvedDependency.argument3) } - func testRegistrationWithThreeArgumentsWithExplicitType() async { - await container.register(type: DependencyWithThreeArguments.self) { _, argument1, argument2, argument3 in + @Test("Registration with three arguments with explicit type") + func registrationWithThreeArgumentsWithExplicitType() async { + // Given + let subject = AsyncContainer() + await subject.register(type: DependencyWithThreeArguments.self) { _, argument1, argument2, argument3 in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" let argument3 = 42 - let resolvedDependency: DependencyWithThreeArguments = await container.resolve(arguments: argument1, argument2, argument3) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") - XCTAssertEqual(argument3, resolvedDependency.argument3, "Container returned dependency with different third argument") + // When + let resolvedDependency: DependencyWithThreeArguments = await subject.resolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) + #expect(argument3 == resolvedDependency.argument3) } - func testUnmatchingArgumentType_ThreeArguments() async { - await container.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in + @Test("Unmatching argument type - three arguments") + func unmatchingArgumentType_ThreeArguments() async throws { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = 48 let argument2 = "test" let argument3 = 42 + // When do { - _ = try await container.tryResolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) - - XCTFail("Expected to throw error") + _ = try await subject.tryResolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + Issue.record("Expected to throw error") } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testRegistrationWithAsyncInitWithTwoArguments() async { - await container.register { _, argument1, argument2 -> DependencyWithAsyncInitWithTwoArguments in - await DependencyWithAsyncInitWithTwoArguments(argument1: argument1, argument2: argument2) + @Test("Registration with async initialization") + func registrationWithAsyncInit() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument -> DependencyWithAsyncInitWithParameter in + await DependencyWithAsyncInitWithParameter(subDependency: argument) } + let argument = StructureDependency(property1: "48") + + // When + let resolvedDependency: DependencyWithAsyncInitWithParameter = await subject.resolve(type: DependencyWithAsyncInitWithParameter.self, arguments: argument) + // Then + #expect(argument == resolvedDependency.subDependency) + } + + @Test("Registration with async initialization and two arguments") + func registrationWithAsyncInitWithTwoArguments() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2 -> DependencyWithAsyncInitWithTwoArguments in + await DependencyWithAsyncInitWithTwoArguments(argument1: argument1, argument2: argument2) + } let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" - let resolvedDependency: DependencyWithAsyncInitWithTwoArguments = await container.resolve(arguments: argument1, argument2) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") + // When + let resolvedDependency: DependencyWithAsyncInitWithTwoArguments = await subject.resolve(type: DependencyWithAsyncInitWithTwoArguments.self, arguments: argument1, argument2) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) } - func testRegistrationWithAsyncInitWithThreeArguments() async { - await container.register { _, argument1, argument2, argument3 -> DependencyWithAsyncInitWithThreeArguments in + @Test("Registration with async initialization and three arguments") + func registrationWithAsyncInitWithThreeArguments() async { + // Given + let subject = AsyncContainer() + await subject.register { _, argument1, argument2, argument3 -> DependencyWithAsyncInitWithThreeArguments in await DependencyWithAsyncInitWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" let argument3 = 42 - let resolvedDependency: DependencyWithAsyncInitWithThreeArguments = await container.resolve(arguments: argument1, argument2, argument3) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") - XCTAssertEqual(argument3, resolvedDependency.argument3, "Container returned dependency with different third argument") + // When + let resolvedDependency: DependencyWithAsyncInitWithThreeArguments = await subject.resolve(type: DependencyWithAsyncInitWithThreeArguments.self, arguments: argument1, argument2, argument3) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) + #expect(argument3 == resolvedDependency.argument3) + } + + @Test("More than three arguments - register succeeds (error only on resolve)") + func moreThanThreeArgumentsRegisterSucceeds() async { + // Given + let subject = AsyncContainer() + + // When / Then - register with 4 arguments does not throw (limit enforced only at resolve) + await subject.register { _, argument1, argument2, argument3, argument4 -> DependencyWithFourArguments in + DependencyWithFourArguments(argument1: argument1, argument2: argument2, argument3: argument3, argument4: argument4) + } + } + + @Test("More than three arguments - tryResolve throws") + func moreThanThreeArgumentsTryResolveThrows() async throws { + // Given + let subject = AsyncContainer() + let argument1 = StructureDependency(property1: "a") + let argument2 = "b" + let argument3 = 42 + let argument4 = true + + // When / Then - tryResolve with 4 arguments throws tooManyArguments + do { + _ = try await subject.tryResolve(type: DependencyWithFourArguments.self, arguments: argument1, argument2, argument3, argument4) + Issue.record("Expected tryResolve to throw") + } catch { + guard let resolutionError = error as? ResolutionError else { + Issue.record("Incorrect error type: \(error)") + return + } + switch resolutionError { + case .tooManyArguments: + #expect(!resolutionError.localizedDescription.isEmpty) + default: + Issue.record("Incorrect resolution error: \(resolutionError)") + } + } } } diff --git a/Tests/Container/Async/AsyncBaseTests.swift b/Tests/Container/Async/AsyncBaseTests.swift index bc17f4b..6123a1a 100644 --- a/Tests/Container/Async/AsyncBaseTests.swift +++ b/Tests/Container/Async/AsyncBaseTests.swift @@ -6,80 +6,79 @@ // import DependencyInjection -import XCTest - -final class AsyncBaseTests: AsyncDITestCase { - func testDependencyRegisteredInDefaultScope() async { - await container.register(in: .shared) { _ -> SimpleDependency in +import Testing + +@Suite("Container/Async/Base Registration", .tags(.async, .base)) +struct AsyncBaseTests { + @Test("Shared dependency") + func sharedDependency() async { + // Given + let subject = AsyncContainer() + await subject.register(in: .shared) { _ -> SimpleDependency in SimpleDependency() } - let resolvedDependency1: SimpleDependency = await container.resolve() - let resolvedDependency2: SimpleDependency = await container.resolve() + // When + let resolvedDependency1: SimpleDependency = await subject.resolve() + let resolvedDependency2: SimpleDependency = await subject.resolve() - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") + // Then + #expect(resolvedDependency1 === resolvedDependency2) } - func testDependencyRegisteredInDefaultScopeWithExplicitType() async { - await container.register(type: SimpleDependency.self, in: .shared) { _ -> SimpleDependency in + @Test("Non-shared dependency") + func nonSharedDependency() async { + // Given + let subject = AsyncContainer() + await subject.register(in: .new) { _ -> SimpleDependency in SimpleDependency() } - let resolvedDependency1: SimpleDependency = await container.resolve() - let resolvedDependency2: SimpleDependency = await container.resolve() + // When + let resolvedDependency1: SimpleDependency = await subject.resolve() + let resolvedDependency2: SimpleDependency = await subject.resolve() - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") + // Then + #expect(resolvedDependency1 !== resolvedDependency2) } - func testSharedDependency() async { - await container.register(in: .shared) { _ -> SimpleDependency in + @Test("Non-shared dependency with explicit type") + func nonSharedDependencyWithExplicitType() async { + // Given + let subject = AsyncContainer() + await subject.register(type: SimpleDependency.self, in: .new) { _ in SimpleDependency() } - let resolvedDependency1: SimpleDependency = await container.resolve() - let resolvedDependency2: SimpleDependency = await container.resolve() + // When + let resolvedDependency1: SimpleDependency = await subject.resolve() + let resolvedDependency2: SimpleDependency = await subject.resolve() - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") + // Then + #expect(resolvedDependency1 !== resolvedDependency2) } - func testNonSharedDependency() async { - await container.register(in: .new) { _ -> SimpleDependency in - SimpleDependency() - } - - let resolvedDependency1: SimpleDependency = await container.resolve() - let resolvedDependency2: SimpleDependency = await container.resolve() + @Test("Unregistered dependency") + func unregisteredDependency() async throws { + // Given + let subject = AsyncContainer() - XCTAssertTrue(resolvedDependency1 !== resolvedDependency2, "Container returned the same instance") - } - - func testNonSharedDependencyWithExplicitType() async { - await container.register(type: SimpleDependency.self, in: .new) { _ in - SimpleDependency() - } - - let resolvedDependency1: SimpleDependency = await container.resolve() - let resolvedDependency2: SimpleDependency = await container.resolve() - - XCTAssertTrue(resolvedDependency1 !== resolvedDependency2, "Container returned the same instance") - } - - func testUnregisteredDependency() async { + // When do { - _ = try await container.tryResolve(type: SimpleDependency.self) - - XCTFail("Expected to fail tryResolve") + _ = try await subject.tryResolve(type: SimpleDependency.self) + Issue.record("Expected to fail tryResolve") } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .dependencyNotRegistered: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error") + Issue.record("Incorrect resolution error") } } } diff --git a/Tests/Container/Async/AsyncComplexTests.swift b/Tests/Container/Async/AsyncComplexTests.swift index be192fe..1563c04 100644 --- a/Tests/Container/Async/AsyncComplexTests.swift +++ b/Tests/Container/Async/AsyncComplexTests.swift @@ -6,95 +6,114 @@ // import DependencyInjection -import XCTest - -final class AsyncComplexTests: AsyncDITestCase { - func testCleanContainer() async { - await container.register(in: .shared) { _ in +import Testing + +@Suite("Container/Async/Complex", .tags(.async, .complex)) +struct AsyncComplexTests { + @Test("Clean container") + func cleanContainer() async throws { + // Given + let subject = AsyncContainer() + await subject.register(in: .shared) { _ in SimpleDependency() } - let resolvedDependency = try? await container.tryResolve(type: SimpleDependency.self) - - XCTAssertNotNil(resolvedDependency, "Couldn't resolve dependency") + // When + let resolvedDependency = try? await subject.tryResolve(type: SimpleDependency.self) + #expect(resolvedDependency != nil) - await container.clean() + await subject.clean() - let unresolvedDependency = try? await container.tryResolve(type: SimpleDependency.self) - - XCTAssertNil(unresolvedDependency, "Dependency wasn't cleaned") + // Then + let unresolvedDependency = try? await subject.tryResolve(type: SimpleDependency.self) + #expect(unresolvedDependency == nil) } - func testReleaseSharedInstances() async { - await container.register(in: .shared) { _ in + @Test("Release shared instances") + func releaseSharedInstances() async { + // Given + let subject = AsyncContainer() + await subject.register(in: .shared) { _ in SimpleDependency() } - var resolvedDependency1: SimpleDependency? = await container.resolve(type: SimpleDependency.self) - weak var resolvedDependency2 = await container.resolve(type: SimpleDependency.self) + // When + var resolvedDependency1: SimpleDependency? = await subject.resolve(type: SimpleDependency.self) + weak var resolvedDependency2 = await subject.resolve(type: SimpleDependency.self) - XCTAssertNotNil(resolvedDependency1, "Shared instance wasn't resolved") - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Different instancies of a shared dependency") + #expect(resolvedDependency1 != nil) + #expect(resolvedDependency1 === resolvedDependency2) - await container.releaseSharedInstances() + await subject.releaseSharedInstances() - let resolvedDependency3 = await container.resolve(type: SimpleDependency.self) + let resolvedDependency3 = await subject.resolve(type: SimpleDependency.self) - XCTAssertFalse(resolvedDependency1 === resolvedDependency3, "Shared instance wasn't released") + // Then + #expect(resolvedDependency1 !== resolvedDependency3) resolvedDependency1 = nil - XCTAssertNil(resolvedDependency2, "Shared instance wasn't released") + #expect(resolvedDependency2 == nil) } - func testReregistration() async { - await container.register(type: DIProtocol.self, in: .shared) { _ in + @Test("Reregistration") + func reregistration() async { + // Given + let subject = AsyncContainer() + await subject.register(type: DIProtocol.self, in: .shared) { _ in SimpleDependency() } - let resolvedSimpleDependency = await container.resolve(type: DIProtocol.self) + // When + let resolvedSimpleDependency = await subject.resolve(type: DIProtocol.self) - XCTAssertTrue(resolvedSimpleDependency is SimpleDependency, "Resolved dependency of wrong type") + // Then + #expect(resolvedSimpleDependency is SimpleDependency) - await container.register(type: DIProtocol.self, in: .shared) { _ in + // When + await subject.register(type: DIProtocol.self, in: .shared) { _ in StructureDependency.default } - let resolvedStructureDependency = await container.resolve(type: DIProtocol.self) + let resolvedStructureDependency = await subject.resolve(type: DIProtocol.self) - XCTAssertTrue(resolvedStructureDependency is StructureDependency, "Resolved dependency of wrong type") + // Then + #expect(resolvedStructureDependency is StructureDependency) } - func testSameDependencyTypeRegisteredWithDifferentTypes() async { - await container.register(type: DIProtocol.self, in: .shared) { _ in + @Test("Same dependency type registered with different types") + func sameDependencyTypeRegisteredWithDifferentTypes() async { + // Given + let subject = AsyncContainer() + await subject.register(type: DIProtocol.self, in: .shared) { _ in StructureDependency(property1: "first") } - await container.register(type: StructureDependency.self, in: .shared) { _ in + await subject.register(type: StructureDependency.self, in: .shared) { _ in StructureDependency(property1: "second") } - let resolvedProtocolDependency: DIProtocol = await container.resolve() - let resolvedTypeDependency: StructureDependency = await container.resolve() - - XCTAssertTrue(resolvedProtocolDependency is StructureDependency, "Resolved dependency of wrong type") - XCTAssertEqual(resolvedTypeDependency.property1, "second", "Resolved dependency from a wrong factory") + // When + let resolvedProtocolDependency: DIProtocol = await subject.resolve() + let resolvedTypeDependency: StructureDependency = await subject.resolve() - XCTAssertNotEqual( - (resolvedProtocolDependency as? StructureDependency)?.property1, - resolvedTypeDependency.property1, - "Resolved same instances" - ) + // Then + #expect(resolvedProtocolDependency is StructureDependency) + #expect(resolvedTypeDependency.property1 == "second") + #expect((resolvedProtocolDependency as? StructureDependency)?.property1 != resolvedTypeDependency.property1) } - func testCombiningSharedAndNonsharedDependencies() async { - await container.register(in: .new) { _ in + @Test("Combining shared and non-shared dependencies") + func combiningSharedAndNonsharedDependencies() async { + // Given + let subject = AsyncContainer() + await subject.register(in: .new) { _ in SimpleDependency() } - await container.register(in: .shared) { + await subject.register(in: .shared) { DependencyWithParameter(subDependency: await $0.resolve()) } - await container.register { + await subject.register { DependencyWithParameter3( subDependency1: await $0.resolve(), subDependency2: $1, @@ -109,36 +128,34 @@ final class AsyncComplexTests: AsyncDITestCase { subDependency: StructureDependency(property1: "second") ) - let resolvedDependency1: DependencyWithParameter = await container.resolve() - let resolvedDependency2: DependencyWithParameter = await container.resolve() - let resolvedDependency3 = await container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) - let resolvedDependency4 = await container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Resolved different instances") - XCTAssertTrue(resolvedDependency1.subDependency === resolvedDependency2.subDependency, "Resolved different instances") - - XCTAssertFalse(resolvedDependency1.subDependency === resolvedDependency3.subDependency1, "Resolved the same instance for a subdependency") - XCTAssertFalse(resolvedDependency3.subDependency1 === resolvedDependency4.subDependency1, "Resolved the same instance for a subdependency") - - XCTAssertFalse(resolvedDependency3 === resolvedDependency4, "Resolved same instances") - - XCTAssertNotEqual( - resolvedDependency3.subDependency2.subDependency.property1, - resolvedDependency4.subDependency2.subDependency.property1, - "Resolved instances with the same argument" - ) + // When + let resolvedDependency1: DependencyWithParameter = await subject.resolve() + let resolvedDependency2: DependencyWithParameter = await subject.resolve() + let resolvedDependency3 = await subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) + let resolvedDependency4 = await subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) + + // Then + #expect(resolvedDependency1 === resolvedDependency2) + #expect(resolvedDependency1.subDependency === resolvedDependency2.subDependency) + #expect(resolvedDependency1.subDependency !== resolvedDependency3.subDependency1) + #expect(resolvedDependency3.subDependency1 !== resolvedDependency4.subDependency1) + #expect(resolvedDependency3 !== resolvedDependency4) + #expect(resolvedDependency3.subDependency2.subDependency.property1 != resolvedDependency4.subDependency2.subDependency.property1) } - func testCombiningSharedAndNonsharedDependenciesWithExplicitFactories() async { - await container.register(in: .new) { _ in + @Test("Combining shared and non-shared dependencies with explicit factories") + func combiningSharedAndNonsharedDependenciesWithExplicitFactories() async { + // Given + let subject = AsyncContainer() + await subject.register(in: .new) { _ in SimpleDependency() } - await container.register(in: .shared) { + await subject.register(in: .shared) { DependencyWithParameter( subDependency: await $0.resolve() ) } - await container.register { resolver, argument in + await subject.register { resolver, argument in DependencyWithParameter3( subDependency1: await resolver.resolve(), subDependency2: argument, @@ -153,23 +170,18 @@ final class AsyncComplexTests: AsyncDITestCase { subDependency: StructureDependency(property1: "second") ) - let resolvedDependency1: DependencyWithParameter = await container.resolve() - let resolvedDependency2: DependencyWithParameter = await container.resolve() - let resolvedDependency3 = await container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) - let resolvedDependency4 = await container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Resolved different instances") - XCTAssertTrue(resolvedDependency1.subDependency === resolvedDependency2.subDependency, "Resolved different instances") - - XCTAssertFalse(resolvedDependency1.subDependency === resolvedDependency3.subDependency1, "Resolved the same instance for a subdependency") - XCTAssertFalse(resolvedDependency3.subDependency1 === resolvedDependency4.subDependency1, "Resolved the same instance for a subdependency") - - XCTAssertFalse(resolvedDependency3 === resolvedDependency4, "Resolved same instances") - - XCTAssertNotEqual( - resolvedDependency3.subDependency2.subDependency.property1, - resolvedDependency4.subDependency2.subDependency.property1, - "Resolved instances with the same argument" - ) + // When + let resolvedDependency1: DependencyWithParameter = await subject.resolve() + let resolvedDependency2: DependencyWithParameter = await subject.resolve() + let resolvedDependency3 = await subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) + let resolvedDependency4 = await subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) + + // Then + #expect(resolvedDependency1 === resolvedDependency2) + #expect(resolvedDependency1.subDependency === resolvedDependency2.subDependency) + #expect(resolvedDependency1.subDependency !== resolvedDependency3.subDependency1) + #expect(resolvedDependency3.subDependency1 !== resolvedDependency4.subDependency1) + #expect(resolvedDependency3 !== resolvedDependency4) + #expect(resolvedDependency3.subDependency2.subDependency.property1 != resolvedDependency4.subDependency2.subDependency.property1) } } diff --git a/Tests/Container/Sync/ArgumentTests.swift b/Tests/Container/Sync/ArgumentTests.swift index 7f7d0d0..9f9313b 100644 --- a/Tests/Container/Sync/ArgumentTests.swift +++ b/Tests/Container/Sync/ArgumentTests.swift @@ -6,186 +6,273 @@ // import DependencyInjection -import XCTest - -final class ContainerArgumentTests: DITestCase { - func testRegistration() { - container.register { _, argument -> DependencyWithValueTypeParameter in +import Testing + +@Suite("Container/Sync/Arguments", .tags(.sync, .arguments)) +struct ContainerArgumentTests { + @Test("Registration with single argument") + func registration() { + // Given + let subject = Container() + subject.register { _, argument -> DependencyWithValueTypeParameter in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithValueTypeParameter = container.resolve(arguments: argument) - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") + // When + let resolvedDependency: DependencyWithValueTypeParameter = subject.resolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + + // Then + #expect(argument == resolvedDependency.subDependency) } - func testRegistrationWithExplicitType() { - container.register(type: DependencyWithValueTypeParameter.self) { _, argument in + @Test("Registration with explicit type") + func registrationWithExplicitType() { + // Given + let subject = Container() + subject.register(type: DependencyWithValueTypeParameter.self) { _, argument in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithValueTypeParameter = container.resolve(arguments: argument) - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") + // When + let resolvedDependency: DependencyWithValueTypeParameter = subject.resolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + + // Then + #expect(argument == resolvedDependency.subDependency) } - func testUnmatchingArgumentType_ZeroArguments() { - container.register(in: .shared) { _ -> SimpleDependency in + @Test("Unmatching argument type - zero arguments") + func unmatchingArgumentType_ZeroArguments() throws { + // Given + let subject = Container() + subject.register { _ -> SimpleDependency in SimpleDependency() } - let argument = 48 - XCTAssertThrowsError( - try container.tryResolve(type: SimpleDependency.self, arguments: argument), - "Resolver didn't throw an error" - ) { error in + // When + do { + _ = try subject.tryResolve(type: SimpleDependency.self, arguments: argument) + Issue.record("Expected to throw error") + } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testUnmatchingArgumentType_OneArgument() { - container.register { _, argument -> DependencyWithValueTypeParameter in + @Test("Unmatching argument type - one argument") + func unmatchingArgumentType_OneArgument() throws { + // Given + let subject = Container() + subject.register { _, argument -> DependencyWithValueTypeParameter in DependencyWithValueTypeParameter(subDependency: argument) } - let argument = 48 - XCTAssertThrowsError( - try container.tryResolve(type: DependencyWithValueTypeParameter.self, arguments: argument), - "Resolver didn't throw an error" - ) { error in + // When + do { + _ = try subject.tryResolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + Issue.record("Expected to throw error") + } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testRegistrationWithTwoArguments() { - container.register { _, argument1, argument2 -> DependencyWithTwoArguments in + @Test("Registration with two arguments") + func registrationWithTwoArguments() { + // Given + let subject = Container() + subject.register { _, argument1, argument2 -> DependencyWithTwoArguments in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" - let resolvedDependency: DependencyWithTwoArguments = container.resolve(arguments: argument1, argument2) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") + // When + let resolvedDependency: DependencyWithTwoArguments = subject.resolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) } - func testRegistrationWithTwoArgumentsWithExplicitType() { - container.register(type: DependencyWithTwoArguments.self) { _, argument1, argument2 in + @Test("Registration with two arguments with explicit type") + func registrationWithTwoArgumentsWithExplicitType() { + // Given + let subject = Container() + subject.register(type: DependencyWithTwoArguments.self) { _, argument1, argument2 in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" - let resolvedDependency: DependencyWithTwoArguments = container.resolve(arguments: argument1, argument2) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") + // When + let resolvedDependency: DependencyWithTwoArguments = subject.resolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) } - func testUnmatchingArgumentType_TwoArguments() { - container.register { _, argument1, argument2 -> DependencyWithTwoArguments in + @Test("Unmatching argument type - two arguments") + func unmatchingArgumentType_TwoArguments() throws { + // Given + let subject = Container() + subject.register { _, argument1, argument2 -> DependencyWithTwoArguments in DependencyWithTwoArguments(argument1: argument1, argument2: argument2) } - let argument1 = 48 let argument2 = "test" - XCTAssertThrowsError( - try container.tryResolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2), - "Resolver didn't throw an error" - ) { error in + // When + do { + _ = try subject.tryResolve(type: DependencyWithTwoArguments.self, arguments: argument1, argument2) + Issue.record("Expected to throw error") + } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error") } } } - func testRegistrationWithThreeArguments() { - container.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in + @Test("Registration with three arguments") + func registrationWithThreeArguments() { + // Given + let subject = Container() + subject.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" let argument3 = 42 - let resolvedDependency: DependencyWithThreeArguments = container.resolve(arguments: argument1, argument2, argument3) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") - XCTAssertEqual(argument3, resolvedDependency.argument3, "Container returned dependency with different third argument") + // When + let resolvedDependency: DependencyWithThreeArguments = subject.resolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) + #expect(argument3 == resolvedDependency.argument3) } - func testRegistrationWithThreeArgumentsWithExplicitType() { - container.register(type: DependencyWithThreeArguments.self) { _, argument1, argument2, argument3 in + @Test("Registration with three arguments with explicit type") + func registrationWithThreeArgumentsWithExplicitType() { + // Given + let subject = Container() + subject.register(type: DependencyWithThreeArguments.self) { _, argument1, argument2, argument3 in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = StructureDependency(property1: "test1") let argument2 = "test2" let argument3 = 42 - let resolvedDependency: DependencyWithThreeArguments = container.resolve(arguments: argument1, argument2, argument3) - XCTAssertEqual(argument1, resolvedDependency.argument1, "Container returned dependency with different first argument") - XCTAssertEqual(argument2, resolvedDependency.argument2, "Container returned dependency with different second argument") - XCTAssertEqual(argument3, resolvedDependency.argument3, "Container returned dependency with different third argument") + // When + let resolvedDependency: DependencyWithThreeArguments = subject.resolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + + // Then + #expect(argument1 == resolvedDependency.argument1) + #expect(argument2 == resolvedDependency.argument2) + #expect(argument3 == resolvedDependency.argument3) } - func testUnmatchingArgumentType_ThreeArguments() { - container.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in + @Test("Unmatching argument type - three arguments") + func unmatchingArgumentType_ThreeArguments() throws { + // Given + let subject = Container() + subject.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) } - let argument1 = 48 let argument2 = "test" let argument3 = 42 - XCTAssertThrowsError( - try container.tryResolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3), - "Resolver didn't throw an error" - ) { error in + // When + do { + _ = try subject.tryResolve(type: DependencyWithThreeArguments.self, arguments: argument1, argument2, argument3) + Issue.record("Expected to throw error") + } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .unmatchingArgumentType: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) + default: + Issue.record("Incorrect resolution error") + } + } + } + + @Test("More than three arguments - register succeeds (error only on resolve)") + func moreThanThreeArgumentsRegisterSucceeds() { + // Given + let subject = Container() + + // When / Then - register with 4 arguments does not throw (limit enforced only at resolve) + subject.register { _, argument1, argument2, argument3, argument4 -> DependencyWithFourArguments in + DependencyWithFourArguments(argument1: argument1, argument2: argument2, argument3: argument3, argument4: argument4) + } + } + + @Test("More than three arguments - tryResolve throws") + func moreThanThreeArgumentsTryResolveThrows() throws { + // Given - register with 3 args is valid + let subject = Container() + subject.register { _, argument1, argument2, argument3 -> DependencyWithThreeArguments in + DependencyWithThreeArguments(argument1: argument1, argument2: argument2, argument3: argument3) + } + let argument1 = StructureDependency(property1: "a") + let argument2 = "b" + let argument3 = 42 + + // When / Then - tryResolve with 4 arguments throws tooManyArguments + do { + _ = try subject.tryResolve(type: DependencyWithFourArguments.self, arguments: argument1, argument2, argument3, true) + Issue.record("Expected tryResolve to throw") + } catch { + guard let resolutionError = error as? ResolutionError else { + Issue.record("Incorrect error type: \(error)") + return + } + switch resolutionError { + case .tooManyArguments: + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error: \(resolutionError)") + Issue.record("Incorrect resolution error: \(resolutionError)") } } } diff --git a/Tests/Container/Sync/AutoregistrationTests.swift b/Tests/Container/Sync/AutoregistrationTests.swift index 1c4dc4b..73b765d 100644 --- a/Tests/Container/Sync/AutoregistrationTests.swift +++ b/Tests/Container/Sync/AutoregistrationTests.swift @@ -6,79 +6,110 @@ // import DependencyInjection -import XCTest - -final class AutoregistrationTests: DITestCase { - func testSharedAutoRegistrationWithoutParameter() { - container.autoregister(initializer: SimpleDependency.init) - - let firstResolved: SimpleDependency = container.resolve() - let secondResolved: SimpleDependency = container.resolve() - - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") +import Testing + +@Suite("Container/Sync/Autoregistration", .tags(.sync, .autoregistration)) +struct AutoregistrationTests { + @Test("Shared auto-registration without parameter") + func sharedAutoRegistrationWithoutParameter() { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + + // When + let firstResolved: SimpleDependency = subject.resolve() + let secondResolved: SimpleDependency = subject.resolve() + + // Then + #expect(firstResolved === secondResolved) } - func testSharedAutoRegistrationOneParameter() { - container.autoregister(initializer: SimpleDependency.init) - container.autoregister(initializer: DependencyWithParameter.init) + @Test("Shared auto-registration with one parameter") + func sharedAutoRegistrationOneParameter() { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) - let firstResolved: DependencyWithParameter = container.resolve() - let secondResolved: DependencyWithParameter = container.resolve() + // When + let firstResolved: DependencyWithParameter = subject.resolve() + let secondResolved: DependencyWithParameter = subject.resolve() - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") + // Then + #expect(firstResolved === secondResolved) } - func testSharedAutoRegistrationTwoParameters() { + @Test("Shared auto-registration with two parameters") + func sharedAutoRegistrationTwoParameters() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.autoregister(initializer: SimpleDependency.init) - container.register(dependency: subDependency) - container.autoregister(initializer: DependencyWithParameter2.init) + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: DependencyWithParameter2.init) - let firstResolved: DependencyWithParameter2 = container.resolve() - let secondResolved: DependencyWithParameter2 = container.resolve() + // When + let firstResolved: DependencyWithParameter2 = subject.resolve() + let secondResolved: DependencyWithParameter2 = subject.resolve() - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") + // Then + #expect(firstResolved === secondResolved) } - func testSharedAutoRegistrationThreeParameters() { + @Test("Shared auto-registration with three parameters") + func sharedAutoRegistrationThreeParameters() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.autoregister(initializer: SimpleDependency.init) - container.register(dependency: subDependency) - container.autoregister(initializer: DependencyWithParameter.init) - container.autoregister(initializer: DependencyWithParameter3.init) + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter3.init) - let firstResolved: DependencyWithParameter3 = container.resolve() - let secondResolved: DependencyWithParameter3 = container.resolve() + // When + let firstResolved: DependencyWithParameter3 = subject.resolve() + let secondResolved: DependencyWithParameter3 = subject.resolve() - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") + // Then + #expect(firstResolved === secondResolved) } - func testSharedAutoRegistrationFourParameters() { + @Test("Shared auto-registration with four parameters") + func sharedAutoRegistrationFourParameters() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.autoregister(initializer: SimpleDependency.init) - container.register(dependency: subDependency) - container.autoregister(initializer: DependencyWithParameter.init) - container.autoregister(initializer: DependencyWithParameter2.init) - container.autoregister(initializer: DependencyWithParameter4.init) - - let firstResolved: DependencyWithParameter4 = container.resolve() - let secondResolved: DependencyWithParameter4 = container.resolve() - - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter2.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter4.init) + + // When + let firstResolved: DependencyWithParameter4 = subject.resolve() + let secondResolved: DependencyWithParameter4 = subject.resolve() + + // Then + #expect(firstResolved === secondResolved) } - func testSharedAutoRegistrationFiveParameters() { + @Test("Shared auto-registration with five parameters") + func sharedAutoRegistrationFiveParameters() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.autoregister(initializer: SimpleDependency.init) - container.register(dependency: subDependency) - container.autoregister(initializer: DependencyWithParameter.init) - container.autoregister(initializer: DependencyWithParameter2.init) - container.autoregister(initializer: DependencyWithParameter3.init) - container.autoregister(initializer: DependencyWithParameter5.init) - - let firstResolved: DependencyWithParameter5 = container.resolve() - let secondResolved: DependencyWithParameter5 = container.resolve() - - XCTAssertTrue(firstResolved === secondResolved, "Container returned different instances") + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter2.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter3.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter5.init) + + // When + let firstResolved: DependencyWithParameter5 = subject.resolve() + let secondResolved: DependencyWithParameter5 = subject.resolve() + + // Then + #expect(firstResolved === secondResolved) } } diff --git a/Tests/Container/Sync/AutoregistrationWithArgumentTest.swift b/Tests/Container/Sync/AutoregistrationWithArgumentTest.swift index b5804bf..7948000 100644 --- a/Tests/Container/Sync/AutoregistrationWithArgumentTest.swift +++ b/Tests/Container/Sync/AutoregistrationWithArgumentTest.swift @@ -6,100 +6,121 @@ // import DependencyInjection -import XCTest - -final class AutoregistrationWithArgumentTest: DITestCase { - func testRegistrationWithoutParameter() { - container.autoregister(argument: StructureDependency.self, initializer: DependencyWithValueTypeParameter.init) - +import Testing + +@Suite("Container/Sync/Autoregistration with Arguments", .tags(.sync, .autoregistration, .arguments)) +struct AutoregistrationWithArgumentTest { + @Test("Registration without parameter") + func registrationWithoutParameter() { + // Given + let subject = Container() + subject.autoregister(argument: StructureDependency.self, initializer: DependencyWithValueTypeParameter.init) let argument = StructureDependency(property1: "48") - let resolvedDependency: DependencyWithValueTypeParameter = container.resolve(arguments: argument) - XCTAssertEqual(argument, resolvedDependency.subDependency, "Container returned dependency with different argument") + // When + let resolvedDependency: DependencyWithValueTypeParameter = subject.resolve(type: DependencyWithValueTypeParameter.self, arguments: argument) + + // Then + #expect(argument == resolvedDependency.subDependency) } - func testRegistrationWithOneParameterFirstPermutation() { + @Test("Registration with one parameter - first permutation") + func registrationWithOneParameterFirstPermutation() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.register(dependency: subDependency) - container.autoregister(argument: SimpleDependency.self, initializer: DependencyWithParameter2.init) - + subject.register(dependency: subDependency) + subject.autoregister(argument: SimpleDependency.self, initializer: DependencyWithParameter2.init) let argument = SimpleDependency() - let firstResolved: DependencyWithParameter2 = container.resolve(arguments: argument) - let secondResolved: DependencyWithParameter2 = container.resolve(arguments: argument) + // When + let firstResolved: DependencyWithParameter2 = subject.resolve(type: DependencyWithParameter2.self, arguments: argument) + let secondResolved: DependencyWithParameter2 = subject.resolve(type: DependencyWithParameter2.self, arguments: argument) - XCTAssertTrue(argument === firstResolved.subDependency1, "Container returned dependency with different argument") - XCTAssertTrue(argument === secondResolved.subDependency1, "Container returned dependency with different argument") - - XCTAssertTrue(firstResolved.subDependency2 === secondResolved.subDependency2, "Different instances of subdependencies") + // Then + #expect(argument === firstResolved.subDependency1) + #expect(argument === secondResolved.subDependency1) + #expect(firstResolved.subDependency2 === secondResolved.subDependency2) } - func testRegistrationWithOneParameterSecondPermutation() { - container.autoregister(initializer: SimpleDependency.init) - container.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter2.init) - + @Test("Registration with one parameter - second permutation") + func registrationWithOneParameterSecondPermutation() { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter2.init) let argument = DependencyWithValueTypeParameter() - let firstResolved: DependencyWithParameter2 = container.resolve(arguments: argument) - let secondResolved: DependencyWithParameter2 = container.resolve(arguments: argument) - - XCTAssertTrue(argument === firstResolved.subDependency2, "Container returned dependency with different argument") - XCTAssertTrue(argument === secondResolved.subDependency2, "Container returned dependency with different argument") + // When + let firstResolved: DependencyWithParameter2 = subject.resolve(type: DependencyWithParameter2.self, arguments: argument) + let secondResolved: DependencyWithParameter2 = subject.resolve(type: DependencyWithParameter2.self, arguments: argument) - XCTAssertTrue(firstResolved.subDependency1 === secondResolved.subDependency1, "Different instances of subdependencies") + // Then + #expect(argument === firstResolved.subDependency2) + #expect(argument === secondResolved.subDependency2) + #expect(firstResolved.subDependency1 === secondResolved.subDependency1) } - func testRegistrationWithTwoParameterFirstPermutation() { + @Test("Registration with two parameters - first permutation") + func registrationWithTwoParameterFirstPermutation() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.register(dependency: subDependency) - container.autoregister(initializer: SimpleDependency.init) - container.autoregister(initializer: DependencyWithParameter.init) - container.autoregister(argument: SimpleDependency.self, initializer: DependencyWithParameter3.init) - + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(argument: SimpleDependency.self, initializer: DependencyWithParameter3.init) let argument = SimpleDependency() - let firstResolved: DependencyWithParameter3 = container.resolve(arguments: argument) - let secondResolved: DependencyWithParameter3 = container.resolve(arguments: argument) - - XCTAssertTrue(argument === firstResolved.subDependency1, "Container returned dependency with different argument") - XCTAssertTrue(argument === secondResolved.subDependency1, "Container returned dependency with different argument") + // When + let firstResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) + let secondResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) - XCTAssertTrue(firstResolved.subDependency2 === secondResolved.subDependency2, "Different instances of subdependencies") - XCTAssertTrue(firstResolved.subDependency3 === secondResolved.subDependency3, "Different instances of subdependencies") + // Then + #expect(argument === firstResolved.subDependency1) + #expect(argument === secondResolved.subDependency1) + #expect(firstResolved.subDependency2 === secondResolved.subDependency2) + #expect(firstResolved.subDependency3 === secondResolved.subDependency3) } - func testRegistrationWithTwoParameterSecondPermutation() { - container.autoregister(initializer: SimpleDependency.init) - container.autoregister(initializer: DependencyWithParameter.init) - container.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter3.init) - + @Test("Registration with two parameters - second permutation") + func registrationWithTwoParameterSecondPermutation() { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter3.init) let argument = DependencyWithValueTypeParameter() - let firstResolved: DependencyWithParameter3 = container.resolve(arguments: argument) - let secondResolved: DependencyWithParameter3 = container.resolve(arguments: argument) + // When + let firstResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) + let secondResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) - XCTAssertTrue(argument === firstResolved.subDependency2, "Container returned dependency with different argument") - XCTAssertTrue(argument === secondResolved.subDependency2, "Container returned dependency with different argument") - - XCTAssertTrue(firstResolved.subDependency1 === secondResolved.subDependency1, "Different instances of subdependencies") - XCTAssertTrue(firstResolved.subDependency3 === secondResolved.subDependency3, "Different instances of subdependencies") + // Then + #expect(argument === firstResolved.subDependency2) + #expect(argument === secondResolved.subDependency2) + #expect(firstResolved.subDependency1 === secondResolved.subDependency1) + #expect(firstResolved.subDependency3 === secondResolved.subDependency3) } - func testRegistrationWithTwoParameterThirdPermutation() { + @Test("Registration with two parameters - third permutation") + func registrationWithTwoParameterThirdPermutation() { + // Given + let subject = Container() let subDependency = DependencyWithValueTypeParameter() - container.register(dependency: subDependency) - container.autoregister(initializer: SimpleDependency.init) - container.autoregister(argument: DependencyWithParameter.self, initializer: DependencyWithParameter3.init) - + subject.register(dependency: subDependency) + subject.autoregister(in: .shared, initializer: SimpleDependency.init) + subject.autoregister(argument: DependencyWithParameter.self, initializer: DependencyWithParameter3.init) let argument = DependencyWithParameter(subDependency: SimpleDependency()) - let firstResolved: DependencyWithParameter3 = container.resolve(arguments: argument) - let secondResolved: DependencyWithParameter3 = container.resolve(arguments: argument) - - XCTAssertTrue(argument === firstResolved.subDependency3, "Container returned dependency with different argument") - XCTAssertTrue(argument === secondResolved.subDependency3, "Container returned dependency with different argument") + // When + let firstResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) + let secondResolved: DependencyWithParameter3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argument) - XCTAssertTrue(firstResolved.subDependency2 === secondResolved.subDependency2, "Different instances of subdependencies") - XCTAssertTrue(firstResolved.subDependency1 === secondResolved.subDependency1, "Different instances of subdependencies") + // Then + #expect(argument === firstResolved.subDependency3) + #expect(argument === secondResolved.subDependency3) + #expect(firstResolved.subDependency2 === secondResolved.subDependency2) + #expect(firstResolved.subDependency1 === secondResolved.subDependency1) } } diff --git a/Tests/Container/Sync/BaseTests.swift b/Tests/Container/Sync/BaseTests.swift index bb0f9cd..cc9ded9 100644 --- a/Tests/Container/Sync/BaseTests.swift +++ b/Tests/Container/Sync/BaseTests.swift @@ -6,106 +6,121 @@ // import DependencyInjection -import XCTest - -final class BaseTests: DITestCase { - func testAutoclosureDependency() { +import Testing + +@Suite("Container/Sync/Base Registration", .tags(.sync, .base)) +struct BaseTests { + @Test("Autoclosure dependency") + func autoclosureDependency() { + // Given + let subject = Container() let dependency = SimpleDependency() - container.register(dependency: dependency) + subject.register(dependency: dependency) - let resolvedDependency: SimpleDependency = container.resolve() + // When + let resolvedDependency: SimpleDependency = subject.resolve() - XCTAssertTrue(dependency === resolvedDependency, "Container returned different instance") + // Then + #expect(dependency === resolvedDependency) } - func testAutoclosureDependencyWithExplicitType() { + @Test("Autoclosure dependency with explicit type") + func autoclosureDependencyWithExplicitType() { + // Given + let subject = Container() let dependency = SimpleDependency() - container.register(type: SimpleDependency.self, dependency: dependency) + subject.register(type: SimpleDependency.self, dependency: dependency) - let resolvedDependency: SimpleDependency = container.resolve() + // When + let resolvedDependency: SimpleDependency = subject.resolve() - XCTAssertTrue(dependency === resolvedDependency, "Container returned different instance") + // Then + #expect(dependency === resolvedDependency) } - func testRepeatedlyResolvedAutoclosureDependency() { - container.register(dependency: SimpleDependency()) - - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") - } - - func testDependencyRegisteredInDefaultScope() { - container.register(in: .shared) { _ -> SimpleDependency in - SimpleDependency() - } - - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") - } - - func testDependencyRegisteredInDefaultScopeWithExplicitType() { - container.register(type: SimpleDependency.self, in: .shared) { _ -> SimpleDependency in - SimpleDependency() - } + @Test("Repeatedly resolved autoclosure dependency") + func repeatedlyResolvedAutoclosureDependency() { + // Given + let subject = Container() + subject.register(dependency: SimpleDependency()) - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() + // When + let resolvedDependency1: SimpleDependency = subject.resolve() + let resolvedDependency2: SimpleDependency = subject.resolve() - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") + // Then + #expect(resolvedDependency1 === resolvedDependency2) } - func testSharedDependency() { - container.register(in: .shared) { _ -> SimpleDependency in + @Test("Shared dependency") + func sharedDependency() { + // Given + let subject = Container() + subject.register(in: .shared) { _ -> SimpleDependency in SimpleDependency() } - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() + // When + let resolvedDependency1: SimpleDependency = subject.resolve() + let resolvedDependency2: SimpleDependency = subject.resolve() - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Container returned different instance") + // Then + #expect(resolvedDependency1 === resolvedDependency2) } - func testNonSharedDependency() { - container.register(in: .new) { _ -> SimpleDependency in + @Test("Non-shared dependency") + func nonSharedDependency() { + // Given + let subject = Container() + subject.register(in: .new) { _ -> SimpleDependency in SimpleDependency() } - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() + // When + let resolvedDependency1: SimpleDependency = subject.resolve() + let resolvedDependency2: SimpleDependency = subject.resolve() - XCTAssertTrue(resolvedDependency1 !== resolvedDependency2, "Container returned the same instance") + // Then + #expect(resolvedDependency1 !== resolvedDependency2) } - func testNonSharedDependencyWithExplicitType() { - container.register(type: SimpleDependency.self, in: .new) { _ in + @Test("Non-shared dependency with explicit type") + func nonSharedDependencyWithExplicitType() { + // Given + let subject = Container() + subject.register(type: SimpleDependency.self, in: .new) { _ in SimpleDependency() } - let resolvedDependency1: SimpleDependency = container.resolve() - let resolvedDependency2: SimpleDependency = container.resolve() + // When + let resolvedDependency1: SimpleDependency = subject.resolve() + let resolvedDependency2: SimpleDependency = subject.resolve() - XCTAssertTrue(resolvedDependency1 !== resolvedDependency2, "Container returned the same instance") + // Then + #expect(resolvedDependency1 !== resolvedDependency2) } - func testUnregisteredDependency() { - XCTAssertThrowsError( - try container.tryResolve(type: SimpleDependency.self), - "Resolver didn't throw an error" - ) { error in + @Test("Unregistered dependency") + func unregisteredDependency() throws { + // Given + let subject = Container() + + // When + do { + _ = try subject.tryResolve(type: SimpleDependency.self) + Issue.record("Expected to throw error") + } catch { + // Then guard let resolutionError = error as? ResolutionError else { - XCTFail("Incorrect error type") + Issue.record("Incorrect error type") return } switch resolutionError { case .dependencyNotRegistered: - XCTAssertNotEqual(resolutionError.localizedDescription, "", "Error description is empty") + #expect(!resolutionError.localizedDescription.isEmpty) default: - XCTFail("Incorrect resolution error") + Issue.record("Incorrect resolution error") } } } diff --git a/Tests/Container/Sync/ComplexTests.swift b/Tests/Container/Sync/ComplexTests.swift index fb78de2..5293dd7 100644 --- a/Tests/Container/Sync/ComplexTests.swift +++ b/Tests/Container/Sync/ComplexTests.swift @@ -6,87 +6,106 @@ // import DependencyInjection -import XCTest +import Testing -final class ComplexTests: DITestCase { - func testCleanContainer() { - container.autoregister(initializer: SimpleDependency.init) +@Suite("Container/Sync/Complex", .tags(.sync, .complex)) +struct ComplexTests { + @Test("Clean container") + func cleanContainer() throws { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) - let resolvedDependency = try? container.tryResolve(type: SimpleDependency.self) + // When + let resolvedDependency = try? subject.tryResolve(type: SimpleDependency.self) + #expect(resolvedDependency != nil) - XCTAssertNotNil(resolvedDependency, "Couldn't resolve dependency") + subject.clean() - container.clean() - - let unresolvedDependency = try? container.tryResolve(type: SimpleDependency.self) - - XCTAssertNil(unresolvedDependency, "Dependency wasn't cleaned") + // Then + let unresolvedDependency = try? subject.tryResolve(type: SimpleDependency.self) + #expect(unresolvedDependency == nil) } - func testReleaseSharedInstances() { - container.autoregister(in: .shared, initializer: SimpleDependency.init) + @Test("Release shared instances") + func releaseSharedInstances() { + // Given + let subject = Container() + subject.autoregister(in: .shared, initializer: SimpleDependency.init) - var resolvedDependency1: SimpleDependency? = container.resolve(type: SimpleDependency.self) - weak var resolvedDependency2 = container.resolve(type: SimpleDependency.self) + // When + var resolvedDependency1: SimpleDependency? = subject.resolve(type: SimpleDependency.self) + weak var resolvedDependency2 = subject.resolve(type: SimpleDependency.self) - XCTAssertNotNil(resolvedDependency1, "Shared instance wasn't resolved") - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Different instancies of a shared dependency") + #expect(resolvedDependency1 != nil) + #expect(resolvedDependency1 === resolvedDependency2) - container.releaseSharedInstances() + subject.releaseSharedInstances() - let resolvedDependency3 = container.resolve(type: SimpleDependency.self) + let resolvedDependency3 = subject.resolve(type: SimpleDependency.self) - XCTAssertFalse(resolvedDependency1 === resolvedDependency3, "Shared instance wasn't released") + // Then + #expect(resolvedDependency1 !== resolvedDependency3) resolvedDependency1 = nil - XCTAssertNil(resolvedDependency2, "Shared instance wasn't released") + #expect(resolvedDependency2 == nil) } - func testReregistration() { - container.register(type: DIProtocol.self, in: .shared) { _ in + @Test("Reregistration") + func reregistration() { + // Given + let subject = Container() + subject.register(type: DIProtocol.self, in: .shared) { _ in SimpleDependency() } - let resolvedSimpleDependency = container.resolve(type: DIProtocol.self) + // When + let resolvedSimpleDependency = subject.resolve(type: DIProtocol.self) - XCTAssertTrue(resolvedSimpleDependency is SimpleDependency, "Resolved dependency of wrong type") + // Then + #expect(resolvedSimpleDependency is SimpleDependency) - container.register(type: DIProtocol.self, in: .shared) { _ in + // When + subject.register(type: DIProtocol.self, in: .shared) { _ in StructureDependency.default } - let resolvedStructureDependency = container.resolve(type: DIProtocol.self) + let resolvedStructureDependency = subject.resolve(type: DIProtocol.self) - XCTAssertTrue(resolvedStructureDependency is StructureDependency, "Resolved dependency of wrong type") + // Then + #expect(resolvedStructureDependency is StructureDependency) } - func testSameDependencyTypeRegisteredWithDifferentTypes() { - container.register(type: DIProtocol.self, in: .shared) { _ in + @Test("Same dependency type registered with different types") + func sameDependencyTypeRegisteredWithDifferentTypes() { + // Given + let subject = Container() + subject.register(type: DIProtocol.self, in: .shared) { _ in StructureDependency(property1: "first") } - container.register(type: StructureDependency.self, in: .shared) { _ in + subject.register(type: StructureDependency.self, in: .shared) { _ in StructureDependency(property1: "second") } - let resolvedProtocolDependency: DIProtocol = container.resolve() - let resolvedTypeDependency: StructureDependency = container.resolve() + // When + let resolvedProtocolDependency: DIProtocol = subject.resolve() + let resolvedTypeDependency: StructureDependency = subject.resolve() - XCTAssertTrue(resolvedProtocolDependency is StructureDependency, "Resolved dependency of wrong type") - XCTAssertEqual(resolvedTypeDependency.property1, "second", "Resolved dependency from a wrong factory") - - XCTAssertNotEqual( - (resolvedProtocolDependency as? StructureDependency)?.property1, - resolvedTypeDependency.property1, - "Resolved same instances" - ) + // Then + #expect(resolvedProtocolDependency is StructureDependency) + #expect(resolvedTypeDependency.property1 == "second") + #expect((resolvedProtocolDependency as? StructureDependency)?.property1 != resolvedTypeDependency.property1) } - func testCombiningSharedAndNonsharedDependencies() { - container.autoregister(in: .new, initializer: SimpleDependency.init) - container.autoregister(in: .shared, initializer: DependencyWithParameter.init) - container.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter3.init) + @Test("Combining shared and non-shared dependencies") + func combiningSharedAndNonsharedDependencies() { + // Given + let subject = Container() + subject.autoregister(in: .new, initializer: SimpleDependency.init) + subject.autoregister(in: .shared, initializer: DependencyWithParameter.init) + subject.autoregister(argument: DependencyWithValueTypeParameter.self, initializer: DependencyWithParameter3.init) let argumentDependency1 = DependencyWithValueTypeParameter( subDependency: StructureDependency(property1: "first") @@ -95,36 +114,34 @@ final class ComplexTests: DITestCase { subDependency: StructureDependency(property1: "second") ) - let resolvedDependency1: DependencyWithParameter = container.resolve() - let resolvedDependency2: DependencyWithParameter = container.resolve() - let resolvedDependency3 = container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) - let resolvedDependency4 = container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Resolved different instances") - XCTAssertTrue(resolvedDependency1.subDependency === resolvedDependency2.subDependency, "Resolved different instances") - - XCTAssertFalse(resolvedDependency1.subDependency === resolvedDependency3.subDependency1, "Resolved the same instance for a subdependency") - XCTAssertFalse(resolvedDependency3.subDependency1 === resolvedDependency4.subDependency1, "Resolved the same instance for a subdependency") - - XCTAssertFalse(resolvedDependency3 === resolvedDependency4, "Resolved same instances") - - XCTAssertNotEqual( - resolvedDependency3.subDependency2.subDependency.property1, - resolvedDependency4.subDependency2.subDependency.property1, - "Resolved instances with the same argument" - ) + // When + let resolvedDependency1: DependencyWithParameter = subject.resolve() + let resolvedDependency2: DependencyWithParameter = subject.resolve() + let resolvedDependency3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) + let resolvedDependency4 = subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) + + // Then + #expect(resolvedDependency1 === resolvedDependency2) + #expect(resolvedDependency1.subDependency === resolvedDependency2.subDependency) + #expect(resolvedDependency1.subDependency !== resolvedDependency3.subDependency1) + #expect(resolvedDependency3.subDependency1 !== resolvedDependency4.subDependency1) + #expect(resolvedDependency3 !== resolvedDependency4) + #expect(resolvedDependency3.subDependency2.subDependency.property1 != resolvedDependency4.subDependency2.subDependency.property1) } - func testCombiningSharedAndNonsharedDependenciesWithExplicitFactories() { - container.register(in: .new) { _ in + @Test("Combining shared and non-shared dependencies with explicit factories") + func combiningSharedAndNonsharedDependenciesWithExplicitFactories() { + // Given + let subject = Container() + subject.register(in: .new) { _ in SimpleDependency() } - container.register(in: .shared) { resolver in + subject.register(in: .shared) { resolver in DependencyWithParameter( subDependency: resolver.resolve() ) } - container.register { resolver, argument in + subject.register { resolver, argument in DependencyWithParameter3( subDependency1: resolver.resolve(), subDependency2: argument, @@ -139,23 +156,18 @@ final class ComplexTests: DITestCase { subDependency: StructureDependency(property1: "second") ) - let resolvedDependency1: DependencyWithParameter = container.resolve() - let resolvedDependency2: DependencyWithParameter = container.resolve() - let resolvedDependency3 = container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) - let resolvedDependency4 = container.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) - - XCTAssertTrue(resolvedDependency1 === resolvedDependency2, "Resolved different instances") - XCTAssertTrue(resolvedDependency1.subDependency === resolvedDependency2.subDependency, "Resolved different instances") - - XCTAssertFalse(resolvedDependency1.subDependency === resolvedDependency3.subDependency1, "Resolved the same instance for a subdependency") - XCTAssertFalse(resolvedDependency3.subDependency1 === resolvedDependency4.subDependency1, "Resolved the same instance for a subdependency") - - XCTAssertFalse(resolvedDependency3 === resolvedDependency4, "Resolved same instances") - - XCTAssertNotEqual( - resolvedDependency3.subDependency2.subDependency.property1, - resolvedDependency4.subDependency2.subDependency.property1, - "Resolved instances with the same argument" - ) + // When + let resolvedDependency1: DependencyWithParameter = subject.resolve() + let resolvedDependency2: DependencyWithParameter = subject.resolve() + let resolvedDependency3 = subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency1) + let resolvedDependency4 = subject.resolve(type: DependencyWithParameter3.self, arguments: argumentDependency2) + + // Then + #expect(resolvedDependency1 === resolvedDependency2) + #expect(resolvedDependency1.subDependency === resolvedDependency2.subDependency) + #expect(resolvedDependency1.subDependency !== resolvedDependency3.subDependency1) + #expect(resolvedDependency3.subDependency1 !== resolvedDependency4.subDependency1) + #expect(resolvedDependency3 !== resolvedDependency4) + #expect(resolvedDependency3.subDependency2.subDependency.property1 != resolvedDependency4.subDependency2.subDependency.property1) } } diff --git a/Tests/PropertyWrappers/PropertyWrapperTests.swift b/Tests/PropertyWrappers/PropertyWrapperTests.swift index 8eabb75..0b00608 100644 --- a/Tests/PropertyWrappers/PropertyWrapperTests.swift +++ b/Tests/PropertyWrappers/PropertyWrapperTests.swift @@ -6,16 +6,13 @@ // import DependencyInjection -import XCTest +import Testing -final class PropertyWrapperTests: DITestCase { - override func tearDown() { - Container.shared.clean() - - super.tearDown() - } - - func testInjectionWithSharedContainer() { +@Suite("PropertyWrappers", .tags(.propertyWrappers)) +struct PropertyWrapperTests { + @Test("Injection with shared container") + func injectionWithSharedContainer() { + // Given struct Module { @Injected var resolvedDependency: SimpleDependency } @@ -23,12 +20,20 @@ final class PropertyWrapperTests: DITestCase { let dependency = SimpleDependency() Container.shared.register(dependency: dependency) + // When let module = Module() - XCTAssertTrue(dependency === module.resolvedDependency, "Container returned different instance") + // Then + #expect(dependency === module.resolvedDependency) + + // Cleanup + Container.shared.clean() } - func testInjectionWithCustomContainer() { + @Test("Injection with custom container") + func injectionWithCustomContainer() { + // Given + let subject = Container() struct Module { static var container: Container! @@ -36,47 +41,56 @@ final class PropertyWrapperTests: DITestCase { } let dependency = SimpleDependency() - container.register(dependency: dependency) + subject.register(dependency: dependency) + + Module.container = subject - Module.container = container + // When let module = Module() - XCTAssertTrue(dependency === module.resolvedDependency, "Container returned different instance") + // Then + #expect(dependency === module.resolvedDependency) } - func testLazyInjectionWithSharedContainer() { + @Test("Lazy injection with shared container") + func lazyInjectionWithSharedContainer() { + // Given struct Module { @LazyInjected var resolvedDependency: SimpleDependency } - // 1: Create a module instance + // When let module = Module() - // 2: Only after that register the dependency + // Then let dependency = SimpleDependency() Container.shared.register(dependency: dependency) - // 3: Get resolved dependency - XCTAssertTrue(dependency === module.resolvedDependency, "Container returned different instance") + #expect(dependency === module.resolvedDependency) + + // Cleanup + Container.shared.clean() } - func testLazyInjectionWithCustomContainer() { + @Test("Lazy injection with custom container") + func lazyInjectionWithCustomContainer() { + // Given + let subject = Container() struct Module { static var container: Container! @LazyInjected(from: container) var resolvedDependency: SimpleDependency } - Module.container = container + Module.container = subject - // 1: Create a module instance + // When let module = Module() - // 2: Only after that register the dependency + // Then let dependency = SimpleDependency() - container.register(dependency: dependency) + subject.register(dependency: dependency) - // 3: Get resolved dependency - XCTAssertTrue(dependency === module.resolvedDependency, "Container returned different instance") + #expect(dependency === module.resolvedDependency) } }