• FastAPI(43)- 基于 pytest + requests 进行单元测试


     FastAPI 的单元测试

    • 对于服务端来说,通常会对功能进行单元测试,也称白盒测试
    • FastAPI 集成了第三方库,让我们可以快捷的编写单元测试
    • FastAPI 的单元测试是基于 Pytest + Request 的 

    Pytest 学习

    https://www.cnblogs.com/poloyy/tag/Pytest/

    TestClient 简单的栗子

    #!usr/bin/env python
    # -*- coding:utf-8 _*-
    """
    # author: 小菠萝测试笔记
    # blog:  https://www.cnblogs.com/poloyy/
    # time: 2021/9/29 10:55 下午
    # file: 37_pytest.py
    """
    import uvicorn
    from fastapi import FastAPI
    from fastapi.testclient import TestClient
    
    app = FastAPI()
    
    
    @app.get("/")
    async def read_main():
        return {"msg": "Hello World"}
    
    
    # 声明一个 TestClient,把 FastAPI() 实例对象传进去
    client = TestClient(app)
    
    
    # 测试用
    def test_read_main():
        # 请求 127.0.0.1:8080/
        response = client.get("/")
        assert response.status_code == 200
        assert response.json() == {"msg": "Hello World"}
    
    
    if __name__ == '__main__':
        uvicorn.run(app="37_pytest:app", reload=True, host="127.0.0.1", port=8080)

      

    在该文件夹下的命令行敲

    pytest 37_pytest.py

    运行结果

    TestClient 的源码解析

    继承了 requests 库的 Session

    所以可以像使用 requests 库一样使用 TestClient,拥有 requests 所有方法、属性

    重写了 Session.requests 方法

    重写了 requests 方法,不过只是加了一句 url = urljoin(self.base_url, url) url 拼接代码,还有给函数参数都加了类型指示,更加完善啦~

    自定义 websocket 连接方法

    后面学到 webSocket 再详细讲他

    重写了 __enter__、__exit__ 方法

    • Session 的这两个方法还是比较简陋的,TestClient 做了一次重写,主要是为了添加异步的功能(异步测试后面详解,这篇举栗子的都是普通函数 def)
    • 前面讲过有 __enter__、__exit__ 方法的对象都是上下文管理器,可以用 with .. as .. 语句来调用上下文管理器

    .get() 方法

    上面代码 client.get(),直接调用的就是 Session 提供的 get() 方法啦!

    复杂的测试场景

    服务端

    #!usr/bin/env python
    # -*- coding:utf-8 _*-
    """
    # author: 小菠萝测试笔记
    # blog:  https://www.cnblogs.com/poloyy/
    # time: 2021/9/29 10:55 下午
    # file: s37_pytest.py
    """
    import uvicorn
    from fastapi import FastAPI
    from fastapi.testclient import TestClient
    
    app = FastAPI()
    
    
    @app.get("/")
    async def read_main():
        return {"msg": "Hello World"}
    
    
    # 声明一个 TestClient,把 FastAPI() 实例对象传进去
    client = TestClient(app)
    
    
    # 测试用
    def test_read_main():
        # 请求 127.0.0.1:8080/
        response = client.get("/")
        assert response.status_code == 200
        assert response.json() == {"msg": "Hello World"}
    
    
    from typing import Optional
    
    from fastapi import FastAPI, Header, HTTPException
    from pydantic import BaseModel
    
    # 模拟真实 token
    fake_secret_token = "coneofsilence"
    
    # 模拟真实数据库
    fake_db = {
        "foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
        "bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
    }
    
    app = FastAPI()
    
    
    class Item(BaseModel):
        id: str
        title: str
        description: Optional[str] = None
    
    
    # 接口一:查询数据
    @app.get("/items/{item_id}", response_model=Item)
    async def read_main(item_id: str, x_token: str = Header(...)):
        # 1、校验 token 失败
        if x_token != fake_secret_token:
            raise HTTPException(status_code=400, detail="x-token 错误")
    
        # 2、若数据库没有对应数据
        if item_id not in fake_db:
            raise HTTPException(status_code=404, detail="找不到 item_id")
        # 3、找到数据则返回
        return fake_db[item_id]
    
    
    # 接口二:创建数据
    @app.post("/items/", response_model=Item)
    async def create_item(item: Item, x_token: str = Header(...)):
        # 1、校验 token 失败
        if x_token != fake_secret_token:
            raise HTTPException(status_code=400, detail="x-token 错误")
    
        # 2、若数据库已经存在相同 id 的数据
        if item.id in fake_db:
            raise HTTPException(status_code=400, detail="找不到 item_id")
    
        # 3、添加数据到数据库
        fake_db[item.id] = item
    
        # 4、返回添加的数据
        return item
    
    
    if __name__ == '__main__':
        uvicorn.run(app="s37_test_pytest:app", reload=True, host="127.0.0.1", port=8080)

    单元测试 

    #!usr/bin/env python
    # -*- coding:utf-8 _*-
    """
    # author: 小菠萝测试笔记
    # blog:  https://www.cnblogs.com/poloyy/
    # time: 2021/9/29 10:55 下午
    # file: s37_pytest.py
    """
    from fastapi.testclient import TestClient
    from .s37_test_pytest import app
    
    client = TestClient(app)
    
    
    def test_read_item():
        expect = {"id": "foo", "title": "Foo", "description": "There goes my hero"}
        headers = {"x-token": "coneofsilence"}
        resp = client.get("/items/foo", headers=headers)
        assert resp.status_code == 200
        assert resp.json() == expect
    
    
    def test_read_item_error_header():
        expect = {"detail": "x-token 错误"}
        headers = {"x-token": "test"}
        resp = client.get("/items/foo", headers=headers)
        assert resp.status_code == 400
        assert resp.json() == expect
    
    
    def test_read_item_error_id():
        expect = {"detail": "找不到 item_id"}
        headers = {"x-token": "coneofsilence"}
        resp = client.get("/items/foos", headers=headers)
        assert resp.status_code == 404
        assert resp.json() == expect
    
    
    def test_create_item():
        body = {"id": "foos", "title": "Foo", "description": "There goes my hero"}
        headers = {"x-token": "coneofsilence"}
        resp = client.post("/items/", json=body, headers=headers)
        assert resp.status_code == 200
        assert resp.json() == body
    
    
    def test_create_item_error_header():
        body = {"id": "foo", "title": "Foo", "description": "There goes my hero"}
        expect = {"detail": "x-token 错误"}
        headers = {"x-token": "test"}
        resp = client.post("/items/", json=body, headers=headers)
        assert resp.status_code == 400
        assert resp.json() == expect
    
    
    def test_create_item_error_id():
        expect = {"detail": "找不到 item_id"}
        body = {"id": "foo", "title": "Foo", "description": "There goes my hero"}
        headers = {"x-token": "coneofsilence"}
        resp = client.post("/items/", json=body, headers=headers)
        assert resp.status_code == 400
        assert resp.json() == expect

    命令行运行

    pytest test.py -sq

      

    运行结果

    > pytest s37_pytest.py -sq
    ......
    6 passed in 0.40s
  • 相关阅读:
    idea 导入maven项目各种红
    基于 Spring Security 的开源统一角色访问控制系统 URACS
    MySQL读写分离又一好办法 使用 com.mysql.jdbc.ReplicationDriver
    [转]CVS SVN VSS 使用对比
    推荐一个免费开源的虚拟机VBox(VirtualBox)
    JavaScript的对象属性的反射
    [转]需求分析的目的
    尝鲜安装iOS6及新特性
    EXP00003: 未找到段 (4,571) 的存储定义
    QQ邮箱里可以定阅博客园的文章了
  • 原文地址:https://www.cnblogs.com/poloyy/p/15354901.html
Copyright © 2020-2023  润新知