在我的应用程序中,我有一个类封装了一个Service
,并有返回资源和请求的方法。在我的测试中,我想模拟请求和资源的成功/失败,而不进行任何真正的网络调用。
由于Request
是一个协议,通过返回只调用onSuccess
, onFailure
等的自定义实现很容易做到这一点。
然而,对于返回Resource
的方法并不是那么简单,因为Resource
是一个final类而不是协议。
我想创建一个模拟Resource
,当load()
等被调用时,它不做任何真正的网络请求,并暴露了一些方法来假装成功/失败,从而触发添加到Resource
的观察者。
目前有任何方法可以做到这一点吗?
您有几个选择:
存根NetworkingProvider
使用自定义NetworkingProvider
实现创建您的服务。
// App
var myAppNetworkingProvider: NetworkingProviderConvertible =
URLSessionConfiguration.ephemeral // Siesta default
...
Service(baseURL: "...", networking: myAppNetworkingProvider)
// Tests
myAppNetworkingProvider = NetworkStub()
你的StubbedNetworkingProvider
可以返回单个硬编码的URLResponse
,或者匹配URLRequest
,如果你想一次存根多个响应。
这是大多数应用程序的最佳选择。您可以在Siesta自己的性能测试中看到一个示例。它简单,快速,并提供细粒度的控制,但仍然可以让您测试真实的Siesta行为。
存根网络
Siesta工作与网络存根库,如ohhttpstub, Mockingjay和Nocilla。(Siesta本身使用Nocilla进行自己的内部回归测试,尽管该库具有内部竞争条件,并且在撰写本文时维护得不是特别好,所以我不能全心全意地推荐它。)
存根网络本身具有测试应用程序与底层网络API的完整交互的优势。这种方法可能最适合全面的集成测试,特别是如果您想要记录和重放来自真实API的响应。
自定义资源协议
因为Swift支持追溯建模,Resource
不需要是(或实现)一个可测试的协议。您可以创建一个自己的:
protocol ResourceProtocol {
// Resource methods your app uses
}
// No new methods; just adding conformance
extension Resource: ResourceProtocol { }
这听起来最像你在原来的问题中寻找的。但是,我并不特别推荐它:
- 这是最复杂的实现-和最容易出错。你会发现要准确地模仿西斯塔的所有行为是非常困难的。相信我:资源API一开始看起来很无辜,但如果你试图用这种方式来运行你的整个应用程序,你会发现自己重新实现了一半的库。
- 它很可能会错过问题,而不是捕获回归。使用Siesta的许多危险点都与调用的确切顺序有关:哪些事件发生,以什么顺序发生,立即发生什么与在主运行循环的后续回合发生什么,观察者/所有者关系是否创建保留周期,等等。您必须对所有这些事情做出假设,并且您最终将根据您的假设测试代码-而不是根据库的实际行为。
简而言之,与其他方法相比,它是低价值的高努力。这当然不是进行回归测试的有效方法。
也就是说,如果你坚持一个纯粹的"不测试超越边界"的单元测试哲学,那么这就是做它的方法。
我正在编写一个使用Siesta的应用程序,我一直在使用URLMock来模拟Siesta发出的网络请求。我对结果很满意(它只是做了我想要的,没有太多的麻烦),但我相信其他库也能工作。我建议使用网络模拟库,它们具有您可能不会立即想到的特性,例如将其设置为在测试发出意外的网络请求时返回错误。
下面是我如何设置URLMock与Siesta一起工作:
override class func setUp() {
super.setUp()
UMKMockURLProtocol.enable()
UMKMockURLProtocol.setVerificationEnabled(true)
}
override class func tearDown() {
UMKMockURLProtocol.setVerificationEnabled(false)
UMKMockURLProtocol.disable()
super.tearDown()
}
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
UMKMockURLProtocol.reset()
let testConfig = URLSessionConfiguration.ephemeral
testConfig.protocolClasses = [UMKMockURLProtocol.self]
service = Service(baseURL: expectedV2Host, useDefaultTransformers: true, networking: testConfig)
}