0 0
Read Time:3 Minute, 24 Second

前言:

   在本文中我们将探讨在 Rust 的开发下, 如何的针对 Rest API 进行集成测试。

阅读完本文后, 你将能了解:

  • 使用 actix-web 开发端点 (endpoint); /health_check。
  • 针对端点; /health_check; 进行集成测试。

本文:

   当我们收到一个 GET 的 /health_check 请求时, 我们将只返回个 200 OK 的响应; 没有响应信息的本文 (body)。

集成测试会进行以下的检验:

  • health_check 的端点是 /health_check。
  • health_check 的 http 的请求方法是 GET。
  • health_check 的响应是返回 200 OK 的状态。
  • health_check 的响应是个没有本文的信息。

首先, 我们需将 “测试中单元 (Unit Under Test)” 写成一 Rust 的 “Crates”; 因为, 在 Rust 中, 只有将 “测试中单元” 写成是 “Crates”,  “测试中单元” 才能被引进 (import) 到集成测试的代码中; 集成测试的代码才能对 “测试中单元” 进行测试。

图一: 集成测试代码 vs 测试中单元

如图一所示, 测试中单元是位于目录 /src 中的 lib.rs。集成测试的代码是位于目录 /tests 中的 health_check.rs。

Rest API Testing: 测试中单元

测试中单元; /src/lib.rs; 的代码如下:

use actix_web::{web, App, HttpResponse, HttpServer};
use actix_web::dev::Server;
use std::net::TcpListener;

async fn health_check() -> HttpResponse {
    HttpResponse::Ok().finish()
}

// Return 'Server' on the happy path
pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> {
    let server = HttpServer::new(||  {
        App::new()
            .route("/health_check", web::get().to(health_check))
    })
        .listen(listener)?
        .run();

    // No .await here!
    Ok(server)
}
  • Server – HttpServer, 主要是要建立起与 API client 端的连接并且启动 Application – App 来处理 Client 的请求。
  • 我们以 pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> 来创建一新的 Server – HttpServer; 建立起与 API client 端的连接并且启动 Application – App 来处理 Client 的请求。
  • 为了使集成测试的代码; /tests/health_check.rs; 可以调用, 所以, fn run 宣告为 pub。
  • 为了确保 Server – HttpServer 在建立连接时, 不会因为 port 上的占用、冲突, 而导致连接上的失败, 所以, 我们将 Server – HttpServe 在建立连接时的 IP 位址, 设为 fn run 的输入参数; listener: TcpListener; 而不是在 fn run 中写入一个固定的 IP 位址。
  • App::new().route(“/health_check”,web::get().to(health_check));  “/health_check” 指的是路径。
  • web::get().to(health_check); 指的是只有当 http 的请求方法是 GET 时, 才会路由到 async fn health_check() -> HttpResponse; 处理 Client 端的请求。
  • async fn health_check() -> HttpResponse; 处理 Client 端的请求; 返回 200 OK 的状态; 响应一个没有本文的信息。

Rest API Testing: 集成测试

集成测试的代码如下; /tests/health_check.rs

use std::net::TcpListener;

#[tokio::test]
async fn health_check_works() {
    // Arrange HttpServer::run
    let address = spawn_app();

    // We need to bring in 'reqwest'
    // to perform HTTP requests against our application
    let client = reqwest::Client::new();

    // Act
    let response = client
        .get(&format!("{}/health_check", &address))
        .send()
        .await
        .expect("Failed to execute request.");

    // Assert
    assert!(response.status().is_success());
    assert_eq!(Some(0), response.content_length());
}

// Launch our application in the background
// tokio::spawn runs concurrently with down stream futures and tasks; our test logic.
fn spawn_app() -> String {
    let address = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");

    // We retrieve the port assigned to us by the OS
    let port = address.local_addr().unwrap().port();
    let server = zero2prod::run(address).expect("Failed to bind address");

    // Launch the server as a background task
    // tokio::spam returns a handle to the spawned future,
    // but we have no use for it here, hence the non-binding let
    let _ = tokio::spawn(server);

    // We return the application address to the caller!
    format!("http://127.0.0.1:{}", port)
}

Health check 端点的需求规格是:

  • 当我们收到 GET /health_check 的请求时, 我们会返回 200 OK 的状态; 响应一个没有本文的信息。

根据 Health check 端点的需求规格, 集成测试将构建一 Client 的请求; 端点是 /health_check; http 的请求方法是 GET。

// Act
    let response = client
        .get(&format!("{}/health_check", &address))
        .send()
        .await
        .expect("Failed to execute request.");

fn spawn_app() -> String, 主要是有两个主要的任务:

  • 提供 Server – HttpServer 不会因为 port 上的占用、冲突, 而导致连接失败的 IP Address。
 let address = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
  • 使用 tokio::spawn();将 “/src/lib.rs; 测试中单元 (Unit Under Test); ” 的 pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> 在背景 (background) 中执行; 使得集成测试提出 GET /health_check 的请求与测试中单元 (Unit Under Test) 的运行是可以 “同时发生” 的。
图二: 集成测试提出GET /health_check 的请求与测试中单元 (Unit Under Test) 的运行是可以 “同时发生” 的

  • Assert “返回的状态是 200 OK”
assert!(response.status().is_success());
  • Assert “响应的信息是个没有本文的信息”
 assert_eq!(Some(0), response.content_length());

结论:

Rust 语言的 Rest API 的集成测试是很容易的; 只需注意:

  • 需将 “测试中单元 (Unit Under Test)” 写成一 Rust 的 “Crates”。
  • 使用 tokio::spawn(), 使得测试中单元 (Unit Under Test) 在背景 (background) 中执行。
  • 确保 Server – HttpServer 在建立连接时, 不会因为 port 上的占用、冲突, 而导致连接上的失败。

期待你加入 Rustaceans; 期待你加入集成测试!

完整的代码, 请参考: https://github.com/KenFang/HealthCheckIntegrationTest

 

About Post Author

方俊贤; Ken Fang

专利号: 201910652769.4; 一种深度学习的算法, 预测微服务持续发布、持续部署后对产品整体质量的影响, 获得国家知识财产局专利; 符合专利法实施细则第 44 条的规定。
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

发表评论

您的电子邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据