Swift中的HTTP模拟测试示例详解

 更新时间:2023年02月06日 14:49:31   作者:庄周晓梦  
这篇文章主要为大家介绍了Swift中的HTTP模拟测试示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

我们已经了解了单个方法如何为通过网络加载请求提供基础。

然而,网络也是开发应用程序时最大的失败点之一,尤其是在单元测试方面。 当我们编写单元测试时,我们希望测试是可重复的:无论我们执行多少次,我们应该总是得到相同的结果。

如果我们的测试涉及实时网络连接,我们无法保证这一点。 由于我们实际网络请求失败的所有原因,我们的单元测试也可能失败。

因此,我们使用模拟对象来模拟网络连接,但实际上提供了一个一致且可重复的外观,我们可以通过它提供虚假数据。

由于我们已将网络接口抽象为单个方法,因此模拟它非常简单。

这是一个始终返回 200 OK 响应的 HTTPLoading 实现:

public class MockLoader: HTTPLoading {
    public func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void) {
        let urlResponse = HTTPURLResponse(url: request.url!, statusCode: HTTPStatus(rawValue: 200), httpVersion: "1.1", headerFields: nil)!
        let response = HTTPResponse(request: request, response: urlResponse, body: nil)
        completion(.success(response))
    } 
}

我们可以在任何需要 HTTPLoading 值的地方提供 MockLoader 的实例,发送给它的任何请求都将导致 200 OK 响应,尽管主体为 nil

当我们使用模拟网络连接编写单元测试时,我们并不是在测试网络代码本身。 通过模拟网络层,我们将网络作为变量移除,这意味着网络不是被测试的对象:单元测试检查实验的变量。

StarWarsAPI 类

我们将使用我们在上一篇文章中删除的 StarWarsAPI 类来说明这一原则:

public class StarWarsAPI {
    private let loader: HTTPLoading
    public init(loader: HTTPLoading = URLSession.shared) {
        self.loader = loader
    }
    public func requestPeople(completion: @escaping (...) -> Void) {
        var r = HTTPRequest()
        r.host = "swapi.dev"
        r.path = "/api/people"
        loader.load(request: r) { result in
            // TODO: interpret the result
            completion(...)
        }
    }
}

该类的测试将验证其行为:我们要确保它在不同情况下的行为正确。 例如,我们要确保 requestPeople() 方法在收到 200 OK 响应或 404 Not Found 响应或 500 Internal Server Error 时行为正确。 我们使用 MockLoader 模拟这些场景。 这些测试将使我们有信心在不破坏现有功能的情况下改进 StarWarsAPI 的实现。

MockLoader

为了满足这些需求,我们的 MockLoader 需要:

保证传入的请求是我们在测试中期望的请求 为每个请求提供自定义响应 我个人版本的 MockLoader 大致如下所示:

public class MockLoader: HTTPLoading {
    // typealiases help make method signatures simpler
    public typealias HTTPHandler = (HTTPResult) -> Void
    public typealias MockHandler = (HTTPRequest, HTTPHandler) -> Void
    private var nextHandlers = Array<MockHandler>()
    public override func load(request: HTTPRequest, completion: @escaping HTTPHandler) {
        if nextHandlers.isEmpty == false {
            let next = nextHandlers.removeFirst()
            next(request, completion)
        } else {
            let error = HTTPError(code: .cannotConnect, request: request)
            completion(.failure(error))
        }
    }
    @discardableResult
    public func then(_ handler: @escaping MockHandler) -> Mock {
        nextHandlers.append(handler)
        return self
    }
}

这个 MockLoader 允许我提供如何响应连续请求的个性化实现。 例如:

func test_sequentialExecutions() {
    let mock = MockLoader()
    for i in 0 ..< 5 {
        mock.then { request, handler in
            XCTAssert(request.path, "/(i)")
            handler(.success(...))
        }
    }
    for i in 0 ..< 5 {
        var r = HTTPRequest()
        r.path = "/(i)"
        mock.load(r) { result in
            XCTAssertEqual(result.response?.statusCode, .ok)
        }
    }
}

如果我们在为 StarWarsAPI 类编写测试时使用这个 MockLoader,它可能看起来像这样(我省略了 XCTestExpectations,因为它们与本次讨论没有直接关系):

class StarWarsAPITests: XCTestCase {
    let mock = MockLoader()
    lazy var api: StarWarsAPI = { StarWarsAPI(loader: mock) }()
    func test_200_OK_WithValidBody() {
        mock.then { request, handler in
            XCTAssertEqual(request.path, "/api/people")
            handler(.success(/* 200 OK with some valid JSON */))
        }
        api.requestPeople { ...
            // assert that "StarWarsAPI" correctly decoded the response
        }
    }
    func test_200_OK_WithInvalidBody() {
        mock.then { request, handler in
            XCTAssertEqual(request.path, "/api/people")
            handler(.success(/* 200 OK but some mangled JSON */))
        }
        api.requestPeople { ... 
            // assert that "StarWarsAPI" correctly realized the response was bad JSON
        }
    }
    func test_404() {
        mock.then { request, handler in
            XCTAssertEqual(request.path, "/api/people")
            handler(.success(/* 404 Not Found */))
        }
        api.requestPeople { ... 
            // assert that "StarWarsAPI" correctly produced an error
        }
    }
    func test_DroppedConnection() {
        mock.then { request, handler in
            XCTAssertEqual(request.path, "/api/people")
            handler(.failure(/* HTTPError of some kind */))
        }
        api.requestPeople { ... 
            // assert that "StarWarsAPI" correctly produced an error
        }
    }
    ...
}

当我们编写这样的测试时,我们将 StarWarsAPI 视为一个“黑匣子”:给定特定的输入条件,它是否总是产生预期的输出结果?

我们的 HTTPLoading 抽象使得交换网络堆栈的实现成为一个简单的改变。 我们所做的只是将 MockLoader 传递给初始化程序而不是 URLSession。 这里的关键是意识到,通过使我们的 StarWarsAPI 依赖于接口 (HTTPLoading) 而不是具体化 (URLSession),我们极大地增强了它的实用性并使其更易于单独使用(和测试)。

这种对特定实现的行为定义的依赖将在我们实现框架的其余部分时很好地为我们服务。 在下一篇文章中,我们会将 HTTPLoading 更改为一个类并添加一个属性,该属性将为我们可以想象的几乎所有可能的网络行为提供基础。

以上就是Swift中的HTTP模拟测试示例详解的详细内容,更多关于Swift HTTP模拟测试的资料请关注脚本之家其它相关文章!

相关文章

  • swift控件工厂类的实现代码

    swift控件工厂类的实现代码

    这篇文章主要为大家详细介绍了swift控件工厂类的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • iOS中Swift指触即开集成Touch ID指纹识别功能的方法

    iOS中Swift指触即开集成Touch ID指纹识别功能的方法

    随着移动支付时代的到来,Touch ID 指纹验证迅速被支付宝,微信钱包普及,相信各位朋友使用后也大呼方便。下面给大家分享iOS中Swift指触即开集成Touch ID指纹识别功能的方法,一起看看吧
    2017-03-03
  • swift内存管理指针类型使用实例详解

    swift内存管理指针类型使用实例详解

    这篇文章主要为大家介绍了swift内存管理指针类型使用实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • swift中的@UIApplicationMain示例详解

    swift中的@UIApplicationMain示例详解

    这篇文章主要给大家介绍了关于swift中@UIApplicationMain的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-12-12
  • NotificationCenter类实现原理

    NotificationCenter类实现原理

    这篇文章主要为大家介绍了NotificationCenter类实现原理源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • swift相册相机的权限处理示例详解

    swift相册相机的权限处理示例详解

    在iOS7以后要打开手机摄像头或者相册的话都需要权限,在iOS9中更是更新了相册相关api的调用,那么下面这篇文章主要给大家介绍了关于swift相册相机权限处理的相关资料,文中给出了详细的示例代码,需要的朋友可以参考下。
    2017-10-10
  • 简单了解Swift语言中的break和continue语句的用法

    简单了解Swift语言中的break和continue语句的用法

    这篇文章主要简单介绍了Swift语言中的break和continue语句的用法,与其他语言的一样用于循环语句流程控制,需要的朋友可以参考下
    2015-11-11
  • Swift使用表格组件实现单列表

    Swift使用表格组件实现单列表

    这篇文章主要为大家详细介绍了Swift使用表格组件实现单列表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • swift指针及内存管理内存绑定实例详解

    swift指针及内存管理内存绑定实例详解

    这篇文章主要为大家介绍了swift指针及内存管理内存绑定实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 在Mac OS的终端中运行Swift应用的方法

    在Mac OS的终端中运行Swift应用的方法

    这篇文章主要介绍了在Mac OS的终端中运行Swift应用的方法,依靠Xcode的REPL功能来实现,需要的朋友可以参考下
    2015-07-07

最新评论