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

如何在自动化测试中后台运行Tonic服务器并正确启停?

Tonic框架自动化测试:后台启动服务器并完成测试

我正在学习Tonic框架,想要编写一个自动化测试:在后台启动Tonic服务器,通过生成的客户端调用接口,测试完成后停止服务器。

尝试方案

当前代码如下:

//...
pub fn build(inv: impl Inventory) -> Result<Router, Box<dyn Error>> {
    let reflection_service = tonic_reflection::server::Builder::configure()
       .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
       .build()?;

    let srv = Server::builder()
       .add_service(InventoryServer::new(inv))
       .add_service(reflection_service);

    Ok(srv)
}

#[tokio::test]
async fn works() -> Result<(), Box<dyn Error>> {
    let addr = "127.0.0.1:9001".parse()?;
    let inventory = InMemoryInventory::default();

    let (handle, registration) = AbortHandle::new_pair();
    _ = Abortable::new(build(inventory)?.serve(addr), registration); // 服务器未启动

    let sku = "test".to_string();
    let add_res = add::add(AddOptions { sku: sku.clone(), price: 0.0, quantity: 0, name: None, description: None }).await?;
    println!("{:?}", add_res);
    let get_res = get::get(GetOptions { sku }).await?;
    println!("{:?}", get_res);

    handle.abort();
    Ok(())
}

我知道问题在于serve的future未被触发,导致服务器无法启动;如果添加await,服务器会在当前线程启动并阻塞线程。

我尝试用tokio::spawn创建新任务:

_ = Abortable::new(tokio::spawn(async move {
    let addr = "127.0.0.1:9001".parse().unwrap();
    let inventory = InMemoryInventory::default();
    build(inventory).unwrap().serve(addr).await
}), registration);

但出现如下错误:

error: future cannot be sent between threads safely
   --> svc-store/src/main.rs:36:37
    |
36  |       _ = Abortable::new(tokio::spawn(async move {
    |  _____________________________________^
37  | |         let addr = "127.0.0.1:9001".parse().unwrap();
38  | |         let inventory = InMemoryInventory::default();
39  | |         build(inventory).unwrap().serve(addr).await
40  | |     }), registration);
    | |_____^ future created by async block is not `Send`
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn std::error::Error`

我仅需要启动一个可安全存储依赖的后台任务,无需线程间通信,现提出两个问题:

  • 实现需求的最简方式是什么?
  • 为何第二种方案不符合Go程序员的预期?

内容的提问来源于stack exchange,提问作者bart-kosmala

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:42:39