如何测试带有异步无限循环的Rust服务端方法?
异步服务测试解决方案
核心问题分析
你当前的测试无法执行主要有两个原因:
start方法上加了#[tokio::main]宏,这个宏是二进制程序入口专用,调用时会直接启动Tokio runtime并阻塞执行无限循环,永远不会返回结果- 普通
#[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
相关产品推荐
相关产品推荐

