fastapi执行一个同步的单元测试

543 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

fastapi执行一个同步的单元测试

  • 在开发过程中,需要自己来编写一系列的单元测试,来保证自己编写的代码的质量
  • 编写我们的第一个同步单元测试
import sys

sys.path.append('.')
import pytest

client = TestClient(bugmaker, base_url='http://127.0.0.1:9999')


class TestUser:
    """测试登录用例"""

    @pytest.mark.parametrize('data', [{
        "username": "Jack",
        "password": "123456"
    }])
    def test_login(self, data):
        """测试登录"""
        response = client.post(url="/oauth/Login", json=data)
        res: dict = response.json()
        assert response.status_code == 200
        assert res.get('msg') == '登录成功'
        assert res.get('detail') is not None and res.get('detail') != {}
# 执行单元测试只需要在命令行执行pytest即可
  • 遇到的问题
  • 如果你是使用view的注册依赖系统来实现的sqlalchemy的数据库session连接,那么你编写了多个测试用例会无法工作
# 错误信息
ERROR    sqlalchemy.pool.impl.AsyncAdaptedQueuePool:base.py:249 Exception closing connection <AdaptedConnection <aiomysql.connection.Connection object at 0x000001A6AC5B83D0>>
Traceback (most recent call last):
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\pool\base.py", line 739, in _finalize_fairy
    fairy._reset(pool)
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\pool\base.py", line 988, in _reset      
    pool._dialect.do_rollback(self)
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\engine\default.py", line 682, in do_rollback
    dbapi_connection.rollback()
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\dialects\mysql\aiomysql.py", line 205, in rollback
    self.await_(self._connection.rollback())
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\util\_concurrency_py3k.py", line 76, in await_only
    return current.driver.switch(awaitable)
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\util\_concurrency_py3k.py", line 129, in greenlet_spawn
    value = await result
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\aiomysql\connection.py", line 358, in rollback     
    await self._read_ok_packet()
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\aiomysql\connection.py", line 331, in _read_ok_packet
    pkt = await self._read_packet()
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\aiomysql\connection.py", line 559, in _read_packet 
    packet_header = await self._read_bytes(4)
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\aiomysql\connection.py", line 596, in _read_bytes  
    data = await self._reader.readexactly(num_bytes)
  File "D:\soft\Python3.9\lib\asyncio\streams.py", line 723, in readexactly
    await self._wait_for_data('readexactly')
  File "D:\soft\Python3.9\lib\asyncio\streams.py", line 517, in _wait_for_data
    await self._waiter
RuntimeError: Task <Task pending name='anyio.from_thread.BlockingPortal._call_func' coro=<BlockingPortal._call_func() running at D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\anyio\from_thread.py:187> cb=[TaskGroup._spawn.<locals>.task_done() at D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\anyio\_backends\_asyncio.py:629]> got Future <Future pending> attached to a different loop

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\pool\base.py", line 247, in _close_connection
    self._dialect.do_close(connection)
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\engine\default.py", line 688, in do_close
    dbapi_connection.close()
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\sqlalchemy\dialects\mysql\aiomysql.py", line 212, in close
    self._connection.close()
  File "D:\softtest_python_scripts\PlatForm\bug_maker\bugmaker\lib\site-packages\aiomysql\connection.py", line 298, in close        
    self._writer.transport.close()
  File "D:\soft\Python3.9\lib\asyncio\selector_events.py", line 700, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "D:\soft\Python3.9\lib\asyncio\base_events.py", line 746, in call_soon
    self._check_closed()
  File "D:\soft\Python3.9\lib\asyncio\base_events.py", line 510, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
  • 解决方案在当前目录的conftest文件下新建TestClient和
# 修改设置优先循环事件
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

# 解决当接口中涉及依赖注入的数据库使用
async def start_db():
    """解决当接口中涉及依赖注入的数据库使用"""
    async with async_engine.begin() as conn:
        pass
    await async_engine.dispose()

@pytest.fixture(scope="module")
def client() -> Generator:
    """客户端"""
    with TestClient(app=bugmaker, base_url='http://127.0.0.1:9999') as cli:
        # await start_db()
        yield cli
        # await async_engine.dispose()