我如何模拟资源响应?

How can I mock resource responses?

在我的应用程序中,我有一个 class,它封装了一个 Service 并具有 return 资源和请求的方法。在我的测试中,我想模拟 success/failures 请求和资源,而不进行任何真正的网络调用。
因为 Request 是一个协议,所以很容易通过 return 只调用 onSuccessonFailure 等的自定义实现

然而,return 和 Resource 的方法并不是那么简单,因为 Resource 是最终的 class 而不是协议。
我想创建一个模拟 Resource,它在调用 load() 等时不执行任何真正的网络请求,并公开一些方法来伪造一个 success/failure 来触发观察者添加到Resource.

目前有什么办法吗?

您有多种选择:

存根NetworkingProvider

使用自定义 NetworkingProvider 实现创建您的服务。

// App

var myAppNetworkingProvider: NetworkingProviderConvertible =
    URLSessionConfiguration.ephemeral  // Siesta default
...
Service(baseURL: "...", networking: myAppNetworkingProvider)

// Tests

myAppNetworkingProvider = NetworkStub()

您的 StubbedNetworkingProvider 可以 return 单个硬编码 URLResponse,或者匹配 URLRequest 如果您想一次存根多个响应。

这是大多数应用的最佳选择。您可以在 Siesta’s own performance tests 中看到它的示例。它简单、快速,并提供细粒度控制,但仍可让您使用真实的 Siesta 行为进行测试。

存根网络

Siesta 与 OHHTTPStubs, Mockingjay, and Nocilla 等网络存根库一起使用。 (Siesta 本身使用 Nocilla 进行自己的内部回归测试,尽管该库有内部竞争条件并且在撰写本文时维护得不是特别好,所以我不能全心全意地推荐它。)

存根网络本身具有测试您的应用程序与底层网络的完整交互的优势 API。这种方法可能最适合全面的集成测试,特别是如果您想记录和重放来自真实 API.

的响应时

自定义资源协议

因为 Swift 支持 retroactive modelingResource 不需要成为(或实施)可测试的协议。您可以自己创建一个:

protocol ResourceProtocol {
  // Resource methods your app uses
}

// No new methods; just adding conformance
extension Resource: ResourceProtocol { }

这听起来最像您在原始问题中寻找的内容。不过我不是特别推荐:

  • 它实施起来最复杂,也是最容易出错的。你会发现准确模仿 Siesta 的所有行为非常困难。相信我:Resource API 一开始看起来很无辜,但如果您尝试以这种方式练习整个应用程序,您会发现自己重新实现了一半的库。
  • 它很可能会遗漏问题而不会发现回归。使用 Siesta 的许多危险点都与调用的确切顺序有关:哪些事件发生,以什么顺序发生,立即发生什么与主 运行 循环的后续回合发生什么,什么 observer/owner关系会或不会创建保留周期等。您必须对所有这些事情做出假设,并且您最终将根据您的假设测试您的代码,而不是根据库的真实行为。

简而言之,与其他方法相比,它付出的努力更多,价值更低。这当然不是进行回归测试的有效方法。

就是说,如果您秉承纯粹主义者“不要测试越界”的单元测试理念,那么这就是方法。

我正在编写一个使用 Siesta 的应用程序,我一直在使用 URLMock 模拟 Siesta 发出的网络请求。我对结果很满意(它只是做我想做的,没有太多麻烦),但我相信其他库也能工作。我建议使用网络模拟库,它们具有您可能不会立即想到的功能,例如如果测试发出意外的网络请求,则将其设置为 return 错误。

以下是我如何设置 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)
}