如何模拟资源响应



在我的应用程序中,我有一个类封装了一个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)
}

最新更新