fastapi+tortoise-orm的结构,单元测试这一块讲述的都比较不清晰,而且都只是直接丢代码,没有讲原因。 实际上,tortoise-orm在执行的时候,只会被初始化一次,所以在单元测试之前,需要先进行一次初始化,创建一个测试数据库,然后在测试完毕之后删除其测试数据库。 tortoise-orm官方的fastapi实例的测试在部分情况是无法运行的,会报错,特别是在引入一些例如ReArq这样的异步缓存包之后,执行就很容易报错了。 这里我记录一下自己的单元测试编写记录 注意:由于异步的特有问题,应写一个总的测试test函数,来调用所有分的async test函数,入口只能有这一个,否则就会报错=、=。
conftest.py文件主要是配置单元测试的一部分需要经常调用或者在很多测试包里面都会执行的一些函数或方法,或者单元测试需要初始化的一些东西。 该文件必须放在根目录。 conftest文件里面需要创建一个初始化的方法,在pytest运行的时候先运行,将tortoise-rom连接到数据库,然后还需要生成一个fastapi会使用的异步loop(保证数据库和tortoise-orm在同一个异步里面)
# conftest.py import asyncio import os import pytest from tortoise import Tortoise, generate_schema_for_client @pytest.fixture(scope="session")#注册为一个模块,这样这个函数只会被调用一次,其他的次数都是直接使用第一次调用的结果,功能上类似于@preporty,将这个函数注册为方法,然后在后面使用的时候直接在参数里面注明就行(请参考后面) def loop(): loop = asyncio.get_event_loop() return loop TORTOISE_ORM = {#tortoise-orm配置,用来替代代码里面的默认配置,除了数据库连接需要修改,其他的一般不需要动 "connections": { "default": { "engine": "tortoise.backends.mysql",#注意,测试数据库如果为memory类型,可能会导致pytest无法卡在最后无法退出(据大佬说sqlite也有这个毛病,没试过不确定)。 "credentials": { "host": "127.0.0.1", "port": 3306, "user": "root",#如果不为root,可能会出现意想不到的bug,我为了这个搞了一下午... "password": "mnbvcxz123", "database": "realman5", "echo": os.getenv("DB_ECHO") == "True", "maxsize": 10, }, }, }, "apps": { "models": { "models": ["realman.models", "aerich.models", "plibs.contrib.fastapi.admin.models"], "default_connection": "default", }, }, } @pytest.fixture(scope="session", autouse=True)#初始化tortoise-orm的连接,autouse自动调用,启动pytest的时候会自动调用 def initialize_tests(loop, request): loop.run_until_complete(Tortoise.init(config=TORTOISE_ORM,_create_db=True)) # 创建数据库,创建一个临时数据库 loop.run_until_complete( generate_schema_for_client(Tortoise.get_connection("default"), safe=True) ) #这里使用回调的方式,在左右测试完毕的时候会删除该数据库 # 尝试删除所有数据库,在所有的数据操作完毕之后回调该方法删除数据库 request.addfinalizer(lambda: loop.run_until_complete(Tortoise._drop_databases()))tests文件夹放在根目录,里面存放所有需要测试的文件。
由于数据库是临时创建的,所以里面必然是空的,需要手动写入一些数据。 测试数据库的读写:
#test_mysql.py import time from asyncio import AbstractEventLoop from fastapi.testclient import TestClient from realman.common import get_jwt_token from realman.main import app from realman.models import HelpCenter async def write_help_center(): await HelpCenter.create(title="测试中心", content="这是测试", category=1) def test_writesql(loop: AbstractEventLoop):#这个loop会调用conftest里面的loop方法生成 loop.run_until_complete(write_help_center())这里建议把创建数据的操作放到之前的自动调用函数里面去,这样创建完数据库可以第一时间把测试数据写入。
测试接口的方式,在fastapi里面有写的比较清楚,使用也很简单。调用fastapi生成一个client,所有的接口访问都走client就ok。 这里和django挺像的,我直接放一段代码大家就知道怎么用了
import time from asyncio import AbstractEventLoop from fastapi.testclient import TestClient from realman.main import app from realman.models import HelpCenter client = TestClient(app) def test_help_center_detail(): response = client.get("/api/help_center/detail", params={"pk": 1}, headers=get_headers()) assert response.status_code==200 print(...)#如果要在输出里面查看print打印,要调用pytest -s,如有更好的方法,欢迎大家交流沟通