Hopoate 是一个轻量级的 iOS 依赖注入框架,可以简化应用程序依赖项的注册和解析。
注册依赖项非常简单,只需在 Hopoate 的共享容器上调用一个函数即可。
DependencyContainer.shared.register(AnalyticsProvider(), for: AnalyticsProviding.self)
在这里,我们为 AnalyticsProviding
协议注册了一个 AnalyticsProvider
实例。此 AnalyticsProvider
实例会被容器缓存,并且每当容器被请求与 AnalyticsProviding
匹配的依赖项时,都会返回该实例。
解析依赖项同样简单
let analyticsProvider = DependencyContainer.shared.resolve(AnalyticsProviding.self)
这将解析我们之前注册的符合 AnalyticsProviding
的依赖项。
为了方便起见,提供了一个属性包装器来自动解析依赖项
@Dependency private var analyticsProviding: AnalyticsProviding
此属性访问相当于 DependencyContainer.shared.resolve(AnalyticsProviding.self)
。
对于可能并非始终注册的依赖项,提供了一个 optionalResolve
函数
let optionalDependency = DependencyContainer.shared.optionalResolve(Maybe.self)
optionalDependency?.perhaps()
这些也可以使用属性包装器访问
@OptionalDependency private var maybe: Maybe?
注册依赖项时,会返回一个不透明的注册令牌,该令牌稍后可用于从容器中删除注册
let token = DependencyContainer.shared.register(service: AnalyticsProviding.self) {
return AnalyticsProvider()
}
/**
Perform work that depends on the registered dependency
*/
DependencyContainer.shared.remove(token)
Hopoate 非常适合单元测试,因为它使用 LIFO 系统进行依赖项解析,即最近注册的给定类型的依赖项是在请求依赖项解析时返回的依赖项。 这允许将模拟实现注册到容器以进行测试,并在测试结束后将其删除。 删除模拟依赖项后,将解析先前为请求的类型注册的任何依赖项。
let mockLogger = MockLogger()
let token = DependencyContainer.shared.register(service: LogProviding.self) {
return mockLogger
}
testedObject.doSomethingThatLogs()
XCTAssertTrue(mockLogger.receivedLogMessage)
DependencyContainer.shared.remove(token)
可以使用来自 HopoateTestingHelpers
库的 MockContainer
来简化注册和取消注册模拟依赖项的过程(SPM 用户需要使用单独的 HopoateTestingHelpersPackage 包)。 模拟容器使用依赖项容器为给定协议注册一个模拟对象,并在取消分配时取消注册模拟对象。 这是一个例子
import XCTest
import HopoateTestingHelpers
@testable import MyApp
final class MyViewControllerTests: XCTestCase {
private var myViewController: MyViewController!
private var mockAnalyticsContainer: MockContainer<AnalyticsProviding, MockAnalyticsProvider>!
override func setUp() {
super.setUp()
mockAnalyticsContainer = MockContainer(MockAnalyticsProvider())
}
override func tearDown() {
myViewController = nil
mockAnalyticsContainer = nil
super.tearDown()
}
func testItSendsAMessageToTheAnalyticsProviderWhenTheButtonIsTapped() {
givenAViewController()
whenTheUserTapsTheButton()
XCTAssertEqual(mockAnalyticsContainer.mock.receivedMessage, "button_tapped")
}
private func givenAViewController() {
myViewController = MyViewController()
}
private func whenTheUserTapsTheButton() {
myViewController.button.sendActions(for: .primaryActionTriggered)
}
}
我们有一个 MockContainer
,它将为 AnalyticsProviding
协议注册一个依赖项。 它将使用 MockAnalyticsProvider
的实例作为依赖项。 在我们的 setUp
方法中,我们创建容器和模拟分析提供程序。
稍后,在我们的测试函数中,我们执行一些代码,这些代码使用注册到依赖项容器的 AnalyticsProviding
依赖项,在这种情况下,当用户点击按钮时。 按钮点击发生后,我们可以通过从模拟容器的 mock
属性访问它,来检查我们的模拟依赖项是否已收到我们期望的内容。
测试运行后,将执行 tearDown
函数,该函数将我们的 mockAnalyticsContainer
设置为 nil
。 反过来,这会从依赖项容器中删除 MockAnalyticsProvider
,使容器保持在测试运行之前的状态。
默认情况下,当服务注册到依赖项容器时,容器会缓存在创建闭包中给出的服务。
DependencyContainer.shared.register(service: AnalyticsProviding.self) {
return AnalyticsProvider() // <- This object will be returned by all calls to DependencyContainer.shared.resolve(AnalyticsProviding.self)
}
如果您不想要此缓存行为,则可以在注册依赖项时禁用它
DependencyContainer.shared.register(service: AnalyticsProviding.self, cacheService: false) {
return AnalyticsProvider() // <- A new instance of AnalyticsProvider will be provided to each call to DependencyContainer.shared.resolve(AnalyticsProviding.self)
}
将以下内容添加到您的包清单中包的依赖项中
.package(name: "Hopoate", url: "https://github.com/darjeelingsteve/hopoate", from: "1.0.0"),
将以下内容添加到您的 Cartfile
中
github "darjeelingsteve/Hopoate"