我正在尝试编写一个实现,以重新使用 Postgres Testcontainer 进行一系列用 Rust 编写的测试。
该结构体定义如下:
use sea_orm::{ConnectOptions, DatabaseConnection, SqlxPostgresConnector};
use std::process::Command;
use testcontainers::runners::AsyncRunner;
use testcontainers::ContainerAsync;
use testcontainers_modules::postgres::Postgres;
use tokio::runtime::Runtime;
use uuid::Uuid;
const USER_NAME: &'static str = "<USERNAME>";
const PASSWORD: &'static str = "<PASSWORD>";
pub struct PostgresTestContainer {
ctn: ContainerAsync<Postgres>,
}
impl PostgresTestContainer {
pub async fn create() -> anyhow::Result<Self> {
// Create the postgres container and return
let ctn = Postgres::default()
.with_user(USER_NAME)
.with_password(PASSWORD)
.start()
.await?;
Ok(Self { ctn })
}
pub async fn new_db(&self) -> anyhow::Result<DatabaseConnection> {
// Expose and provide a port to localhost
let host_port = self.ctn.get_host_port_ipv4(5432).await?;
let db_name = Uuid::new_v4().to_string();
// Create the database url using the host port
let db_url = format!("postgres://{USER_NAME}:{PASSWORD}@localhost:{host_port}/{db_name}");
let db = SqlxPostgresConnector::connect(ConnectOptions::new(&db_url)).await?;
Ok(db)
}
}
当前的测试实现使用
lazy_static
创建容器并将其提供给所有测试:
lazy_static! {
static ref CTN: AsyncOnce<PostgresTestContainer> =
AsyncOnce::new(async { PostgresTestContainer::create().await.expect("Could not create postgres container" });
...
}
#[tokio::test]
async fn login_with_correct_pwd_returns_tokens() -> Result<(), anyhow::Error> {
let db = CTN.get().await.new_db().await?;
...
}
这非常有效,除了我正在努力确定如何在完成后停止容器。我如何编写清理方法,或者如何重新编写实现以考虑使用后容器的清理?
我尝试使用
impl Drop for PostgresTestContainer
来停止容器,但没有成功并且经过大量研究后我了解到静态对象不会调用 drop
。
谢谢!
我查看了 cafce25 的推荐,虽然
#[custom_test_frameworks]
看起来很有希望,但我不想使用不稳定的 Rust。
我决定使用我在这篇文章中看到的解决方案。 首先,我在testcontainer创建方法中添加了一个参数:
pub async fn create(ctn_name: Uuid) -> anyhow::Result<Self> {
// Create the postgres container and return
let ctn = Postgres::default()
.with_user(USER_NAME)
.with_password(PASSWORD)
.with_container_name(ctn_name)
.start().await?;
Ok(Self { ctn })
}
然后,我创建了一个辅助方法:
pub fn stop_active_container(name: &str) {
Command::new("docker")
.arg("rm")
.arg("-f")
.arg(&name)
.status()
.unwrap();
}
然后我结合使用这两种方法以及
ctor
板条箱来处理创建和销毁。
lazy_static! {
static ref CTN_ID: Uuid = Uuid::new_v4();
static ref CTN: AsyncOnce<PostgresTestContainer> = AsyncOnce::new(async { PostgresTestContainer::create(CTN_ID.clone()).await.unwrap() });
...
}
...
#[dtor]
fn cleanup() {
docker_helpers::stop_active_container(CTN_ID.clone());
}
我真的不喜欢这个解决方案,因为它依赖于从命令行调用 Docker,但看起来它可以正常工作。
很高兴看到 Rust 在未来开发其测试工具。拥有设置和拆卸方法将是一个很棒的补充!