You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决asyncua客户端线程启动超时,实现单入口Pytest测试部署

解决pytest中asyncua服务端与客户端单入口启动的超时问题

核心问题定位

超时的根本原因是服务端尚未完成启动初始化、端口监听等操作,客户端就发起了连接请求。要在pytest单入口场景下实现两者协同,关键是确保服务端完全就绪后再触发客户端逻辑,同时合理管理异步任务与线程的调度。

具体实现方案

1. 编写带就绪等待的asyncua.Server Fixture

用pytest的异步fixture管理服务端生命周期,添加短延时确保服务端完成启动:

import pytest
import asyncua
from asyncua import Server, Client
import asyncio
from threading import Thread

@pytest.fixture(scope="module")
async def opcua_server():
    # 初始化服务端
    server = Server()
    await server.init()
    server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
    # 注册自定义命名空间
    await server.register_namespace("http://test.namespace")
    
    # 启动服务端并等待就绪
    async with server:
        # 等待服务端完成端口监听与初始化(可根据实际调整延时长度)
        await asyncio.sleep(0.5)
        yield server

2. 线程内隔离运行客户端异步任务

客户端的异步代码需要在独立线程的事件循环中执行,避免与pytest主事件循环冲突:

def run_client_async_task(task):
    # 为线程创建独立事件循环
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(task)

async def client_connect_and_operate():
    # 客户端连接逻辑,endpoint需与服务端完全一致
    async with Client(url="opc.tcp://localhost:4840/freeopcua/server/", timeout=10) as client:
        # 示例操作:获取根节点并验证
        root_node = client.get_root_node()
        assert root_node.node_id.Identifier == 85
        return root_node

3. 测试用例中协调服务端与客户端

在测试用例中借助fixture获取就绪的服务端,再启动线程执行客户端任务:

@pytest.mark.asyncio
async def test_opcua_client_server_interaction(opcua_server):
    # 创建客户端异步任务
    client_task = client_connect_and_operate()
    # 启动独立线程运行客户端任务
    client_thread = Thread(target=run_client_async_task, args=(client_task,))
    client_thread.start()
    # 等待客户端任务完成
    client_thread.join()

关键注意事项

  • 就绪等待不可少:必须给服务端留足初始化时间,避免连接时服务端未就绪
  • 事件循环隔离:每个线程需使用独立的asyncio事件循环,防止与pytest主循环冲突
  • 端口与端点一致性:服务端和客户端的endpoint必须完全匹配,否则会直接连接失败
  • 超时参数调整:可以给Client增加timeout参数(如示例中的10秒),避免因网络延迟导致误判

替代方案:同一事件循环中并行运行(无需线程)

如果测试场景简单,也可以在pytest的异步测试中,用asyncio.create_task同时运行服务端和客户端任务:

@pytest.mark.asyncio
async def test_opcua_without_thread():
    server = Server()
    await server.init()
    server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
    await server.register_namespace("http://test.namespace")
    
    # 启动服务端后台任务
    server_task = asyncio.create_task(server.run_forever())
    await asyncio.sleep(0.5)  # 等待服务端就绪
    
    try:
        # 执行客户端逻辑
        async with Client(url="opc.tcp://localhost:4840/freeopcua/server/") as client:
            root_node = client.get_root_node()
            assert root_node is not None
    finally:
        # 确保服务端正常停止
        server.stop()
        await server_task

内容的提问来源于stack exchange,提问作者user2416984

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 20:15:12