Rust プロジェクトで Docker 環境で VSCode のインラインテスト実行を可能にする

## はじめに

Rust プロジェクトで VSCode の Rust Analyzer 拡張機能を使用すると、エディタ上で「▶ Run Test」から直接テストを実行できて便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676183/a02dea15-9dc3-fa67-1cfe-d8254fbf6159.png)

しかし、Docker 環境でデータベースに接続するようなプロジェクトの場合、環境の違いによりテストが失敗してしまうことがあります。

この記事では、Docker 環境でも快適にインラインテストを実行できる設定方法を紹介します。

## 環境

- VSCode
- Rust Analyzer 拡張機能
- Docker / Docker Compose
- データベースを使用する Rust プロジェクト

## プロジェクト構成

以下のような構成のプロジェクトを想定します：

```
my-rust-project/
├── .vscode/
│   └── settings.json
├── src/
│   └── lib.rs
├── docker/
│   └── postgres/
│       └── init.sql
├── scripts/
│   └── run_cargo.sh
├── docker-compose.yml
├── Cargo.toml
└── .env
```

## セットアップ手順

### 1. docker-compose.yml の作成

プロジェクトのルートディレクトリに以下の内容で`docker-compose.yml`を作成します：

```yaml
name: myapp # プロジェクト名を指定（これがコンテナ名のプレフィックスになります）

services:
  app:
    build:
      context: .
    working_dir: /app
    volumes:
      - .:/app
      - cargo-cache:/usr/local/cargo/registry
      - target-cache:/app/target
    environment:
      - DATABASE_URL=postgres://postgres:password@db:5432/testdb
    depends_on:
      - db
    tty: true

  db:
    image: postgres:17.2
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=testdb
    volumes:
      - ./docker/postgres:/docker-entrypoint-initdb.d
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  cargo-cache:
  target-cache:
  postgres-data:
```

### 2. スクリプトの作成

`scripts/run_cargo.sh`を以下の内容で作成します：

```bash
#!/bin/sh

set -e

COMPOSE_PROJECT_NAME="myapp"  # docker-compose.yml の name と同じ値を設定
APP_SERVICE_NAME="app"        # docker-compose.yml の services で定義したアプリケーションのサービス名

run_cargo() {
    if docker compose ps --format json | grep -q "\"Name\":\"${COMPOSE_PROJECT_NAME}-${APP_SERVICE_NAME}\".*\"State\":\"running\""; then
        echo "Running cargo in Docker container..."
        docker compose exec ${APP_SERVICE_NAME} cargo "$@"
    else
        echo "Running cargo locally..."
        cargo "$@"
    fi
}

run_cargo "$@"
```

このスクリプトに実行権限を付与しておきます：

```bash
chmod +x scripts/run_cargo.sh
```

### 3. VSCode 設定の追加

`.vscode/settings.json`に以下の設定を追加します：

```json
{
  "rust-analyzer.runnables.command": "./scripts/run_cargo.sh"
}
```

### 4. データベース接続用のテストコード

`src/lib.rs`に以下のようなテストコードを作成します：

```rust
#[cfg(test)]
mod tests {
    use std::env;
    use sqlx::PgPool;

    #[tokio::test]
    async fn test_database_connection() -> Result<(), Box<dyn std::error::Error>> {
        // DATABASE_URL環境変数から接続文字列を取得
        let database_url = env::var("DATABASE_URL")
            .unwrap_or_else(|_| "postgres://postgres:password@localhost:5432/testdb".to_string());

        let pool = PgPool::connect(&database_url).await?;

        // データベースへの接続テスト
        let result = sqlx::query!("SELECT 1 as num")
            .fetch_one(&pool)
            .await?;

        assert_eq!(result.num, 1);
        Ok(())
    }
}
```

## 動作確認

1. Docker 環境を起動します：

```bash
docker compose up -d
```

2. VSCode で`src/lib.rs`を開き、テストコード上にある「▶ Run Test」をクリックします。

テストが Docker 環境内で実行され、データベースに正常に接続できることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676183/e8636ed1-41c4-cf97-bece-1668e4a6bfde.png)


## 仕組み

このセットアップが機能する仕組みを説明します：

1. Rust Analyzer は通常、`cargo test` コマンドを直接実行します。
2. 設定ファイルで `rust-analyzer.runnables.command` を変更することで、代わりに `run_cargo.sh` を実行するようになります。
3. 今回作成した `run_cargo.sh` スクリプトの挙動：
   - まず、指定された Docker コンテナが実行中かチェックします
   - コンテナが実行中の場合、`docker compose exec` を使用してコンテナ内で `cargo` コマンドを実行します
   - コンテナが実行されていない場合、ローカルで `cargo` コマンドを実行します

## まとめ

Docker 環境を使用する Rust プロジェクトでも、これらの設定で VSCode のインラインテスト実行機能を快適に使用できるようになります。

特に、データベース接続を必要とするテストケースの実行が容易になり、開発効率の向上が期待できそうです！

## 参考リンク

- [Rust Analyzer Documentation](https://rust-analyzer.github.io/)
- [VSCode Rust Analyzer Extension](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)