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

如何测试带有异步无限循环的Rust服务端方法?

异步服务测试解决方案

核心问题分析

你当前的测试无法执行主要有两个原因:

  1. start方法上加了#[tokio::main]宏,这个宏是二进制程序入口专用,调用时会直接启动Tokio runtime并阻塞执行无限循环,永远不会返回结果
  2. 普通#[test]无法执行异步函数,你拿到的started只是未执行的Future,就算正确执行也会因为无限循环永远卡住
    另外你现有代码存在逻辑错误:TcpListener::bind放在循环内部,每次处理完一个连接就会重新绑定同一端口,必然会触发绑定失败报错,需要先把绑定逻辑移到循环外部。

修复步骤

1. 调整start方法定义

去掉方法上的#[tokio::main]宏,改为纯async方法,同时修复绑定逻辑的位置:

pub async fn start(mut self) -> Result<(), GenericError> {
    let tmp = self.configuration.clone().hostaddr();
    println!("{:?}", tmp);
    // 绑定逻辑移到循环外,只执行一次
    let listener = TcpListener::bind(tmp).await?;
    loop {
        match listener.accept().await {
            Ok((stream, _addr)) => {
                let backend = Backend::new(&self.configuration, stream);
                self.backends.push(backend);
            }
            Err(e) => todo!("Log error accepting client connection."),
        }
    }
    Ok(())
}

2. 改写测试代码

使用Tokio提供的异步测试宏,将服务放在后台任务运行,模拟客户端连接验证逻辑后主动终止服务:

#[tokio::test]
async fn test_server_default_configuration() {
    let postmaster = Postmaster::default();
    // 提前获取服务监听地址,建议测试配置指定端口为0,让操作系统自动分配空闲端口避免冲突
    let listen_addr = postmaster.configuration.hostaddr();

    // 将服务 spawn 到后台异步任务运行,不会阻塞当前测试线程
    let server_task = tokio::spawn(postmaster.start());

    // 最多等待3秒,尝试连接服务验证启动成功
    let connect_res = tokio::time::timeout(
        std::time::Duration::from_secs(3),
        TcpStream::connect(listen_addr)
    ).await;

    // 验证连接成功,说明服务启动正常
    assert!(connect_res.is_ok(), "服务启动超时");
    assert!(connect_res.unwrap().is_ok(), "服务连接失败");

    // 测试完成主动终止后台服务任务
    server_task.abort();
    // 等待任务退出,忽略取消异常
    let _ = server_task.await;
}

扩展验证建议

如果需要验证连接后Backend的初始化逻辑,可以给Postmaster实现获取当前Backend数量的只读方法,连接成功后调用该方法验证数值符合预期即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 13:36:10