我尝试对异步套接字服务器进行单元测试,并使用pytest-asyncio
使pytest与异步代码库兼容。服务器一旦启动,总是通过while循环发送回复,并且可能花费大部分时间等待client_loop()
中的传入消息。问题是在单元测试框架终止事件循环并发出此警告之前,无法取消此任务:
任务已被销毁,但尚待处理!
任务:<任务待定coro =< Server.new_client()完成,定义在/[...path...]/server.py:16> wait_for = LT;未来待定cb = [< TaskWakeupMethWrapper对象位于0x106d7cbe8>()]>>
我有权访问的唯一任务是由asyncio.create_task()
创建的任务,这似乎不是同一个任务。那个任务看起来像这样:
任务:<任务待定coro =< start_server()运行于 /usr/local/Cellar/python/[...different path ...] / streams.py:86>>
因此,对此任务调用task.cancel(); await task.wait_cancelled()
无效。
如何编写单元测试以便为每个测试干净地启动和启动服务器,而不是切断可能仍在运行的任务?
以下是例子:
test_server.py
import pytest
import asyncio
@pytest.fixture
async def server(event_loop):
from server import Server
the_server = Server()
await the_server.start()
yield the_server
the_server.stop()
@pytest.mark.asyncio
async def test_connect(server):
loop = asyncio.get_event_loop()
reader, writer = await asyncio.open_connection('0.0.0.0', 8888, loop = loop)
writer.write(b'something')
await reader.read(100)
writer.write(b'something else')
await reader.read(100)
assert 1
server.py
import asyncio
class Server():
async def start(self):
loop = asyncio.get_event_loop()
coro = asyncio.start_server(self.new_client, '0.0.0.0', 8888, loop = loop)
task = loop.create_task(coro)
print('\n')
print(task)
self.server = await task
def stop(self):
self.server.close()
async def new_client(self, reader, writer):
await self.client_loop(reader, writer)
async def client_loop(self, reader, writer):
while True:
await reader.read(100)
writer.write(b'reply')
如果你想运行这个例子,只需运行pip3 install pytest-asyncio
,pytest就可以选择这个插件。
答案 0 :(得分:4)
致电await self.server.wait_closed()
后,您必须self.server.close()
。
你的灯具应该是这样的:
@pytest.fixture
async def server(event_loop):
from server import Server
the_server = Server()
await the_server.start()
yield the_server
await the_server.stop()
stop
的{{1}}方法应如下所示:
Server
有关详细信息,请参阅the documentation。
答案 1 :(得分:0)
asyncio.Server.stop()
方法无法完全停止服务器。它只是停止接受新的连接。在关闭之前创建的任何连接将继续执行直到完成。
根据the documentation (强调我的):
停止服务:关闭侦听套接字并将套接字属性设置为None。
代表现有传入客户端连接的套接字保持打开状态。
服务器异步关闭,使用wait_closed()协程等待服务器关闭。
在此示例中,所有连接都将发送到无限client_loop
方法。
更好的解决方案是在new_client()
中创建一个任务集合,负责执行client_loop()
逻辑,而不是直接等待该方法。使用这种方法,可以在stop()
方法中彻底终止所有打开的任务。