mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-18 06:43:05 +08:00
docs: add native Japanese translation of ECC documentation (ja-JP)
Translate everything-claude-code repository to Japanese including: - 17 root documentation files - 60 agent documentation files - 80 command documentation files - 99 rule files across 18 language directories (common, angular, arkts, cpp, csharp, dart, fsharp, golang, java, kotlin, perl, php, python, ruby, rust, swift, typescript, web) - 199 skill documentation files Total: 455 files translated to Japanese with: - Consistent terminology glossary applied throughout - YAML field names preserved in English (name, description, etc.) - Code blocks and examples untouched (comments translated) - Markdown structure and relative links preserved - Professional translation maintaining technical accuracy This translation expands ECC accessibility to Japanese-speaking developers and teams. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
151
docs/ja-JP/rules/rust/coding-style.md
Normal file
151
docs/ja-JP/rules/rust/coding-style.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rs"
|
||||
---
|
||||
# Rust コーディングスタイル
|
||||
|
||||
> このファイルは [common/coding-style.md](../common/coding-style.md) を Rust 固有のコンテンツで拡張します。
|
||||
|
||||
## フォーマット
|
||||
|
||||
- 強制には **rustfmt** を使用 — コミット前に必ず `cargo fmt` を実行する
|
||||
- リントには **clippy** を使用 — `cargo clippy -- -D warnings`(警告をエラーとして扱う)
|
||||
- 4スペースインデント(rustfmt デフォルト)
|
||||
- 最大行幅: 100文字(rustfmt デフォルト)
|
||||
|
||||
## 不変性
|
||||
|
||||
Rust の変数はデフォルトで不変 — これを活用する:
|
||||
|
||||
- デフォルトで `let` を使用する。ミューテーションが必要な場合にのみ `let mut` を使用する
|
||||
- その場でのミューテーションよりも新しい値を返すことを優先する
|
||||
- 関数が割り当てる必要があるかどうかわからない場合は `Cow<'_, T>` を使用する
|
||||
|
||||
```rust
|
||||
use std::borrow::Cow;
|
||||
|
||||
// 良い例 — デフォルトで不変、新しい値を返す
|
||||
fn normalize(input: &str) -> Cow<'_, str> {
|
||||
if input.contains(' ') {
|
||||
Cow::Owned(input.replace(' ', "_"))
|
||||
} else {
|
||||
Cow::Borrowed(input)
|
||||
}
|
||||
}
|
||||
|
||||
// 悪い例 — 不要なミューテーション
|
||||
fn normalize_bad(input: &mut String) {
|
||||
*input = input.replace(' ', "_");
|
||||
}
|
||||
```
|
||||
|
||||
## 命名
|
||||
|
||||
標準的な Rust の規約に従う:
|
||||
- 関数、メソッド、変数、モジュール、クレートには `snake_case`
|
||||
- 型、トレイト、列挙型、型パラメータには `PascalCase`(UpperCamelCase)
|
||||
- 定数とスタティックには `SCREAMING_SNAKE_CASE`
|
||||
- ライフタイム: 短い小文字(`'a`、`'de`)— 複雑な場合は説明的な名前(`'input`)
|
||||
|
||||
## 所有権と借用
|
||||
|
||||
- デフォルトで借用(`&T`)する。格納または消費する必要がある場合にのみ所有権を取得する
|
||||
- 根本原因を理解せずにボローチェッカーを満たすためにクローンしない
|
||||
- 関数パラメータでは `String` よりも `&str`、`Vec<T>` よりも `&[T]` を受け入れる
|
||||
- `String` を所有する必要があるコンストラクタには `impl Into<String>` を使用する
|
||||
|
||||
```rust
|
||||
// 良い例 — 所有権が不要な場合は借用する
|
||||
fn word_count(text: &str) -> usize {
|
||||
text.split_whitespace().count()
|
||||
}
|
||||
|
||||
// 良い例 — Into を使用してコンストラクタで所有権を取得する
|
||||
fn new(name: impl Into<String>) -> Self {
|
||||
Self { name: name.into() }
|
||||
}
|
||||
|
||||
// 悪い例 — &str で十分なのに String を取得する
|
||||
fn word_count_bad(text: String) -> usize {
|
||||
text.split_whitespace().count()
|
||||
}
|
||||
```
|
||||
|
||||
## エラーハンドリング
|
||||
|
||||
- 伝搬には `Result<T, E>` と `?` を使用する — 本番コードでは `unwrap()` を使わない
|
||||
- **ライブラリ**: `thiserror` で型付きエラーを定義する
|
||||
- **アプリケーション**: 柔軟なエラーコンテキストには `anyhow` を使用する
|
||||
- `.with_context(|| format!("failed to ..."))?` でコンテキストを追加する
|
||||
- `unwrap()` / `expect()` はテストと本当に到達不可能な状態にのみ使用する
|
||||
|
||||
```rust
|
||||
// 良い例 — thiserror によるライブラリエラー
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("failed to read config: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("invalid config format: {0}")]
|
||||
Parse(String),
|
||||
}
|
||||
|
||||
// 良い例 — anyhow によるアプリケーションエラー
|
||||
use anyhow::Context;
|
||||
|
||||
fn load_config(path: &str) -> anyhow::Result<Config> {
|
||||
let content = std::fs::read_to_string(path)
|
||||
.with_context(|| format!("failed to read {path}"))?;
|
||||
toml::from_str(&content)
|
||||
.with_context(|| format!("failed to parse {path}"))
|
||||
}
|
||||
```
|
||||
|
||||
## ループよりもイテレータ
|
||||
|
||||
変換にはイテレータチェーンを優先する。複雑な制御フローにはループを使用する:
|
||||
|
||||
```rust
|
||||
// 良い例 — 宣言的で合成可能
|
||||
let active_emails: Vec<&str> = users.iter()
|
||||
.filter(|u| u.is_active)
|
||||
.map(|u| u.email.as_str())
|
||||
.collect();
|
||||
|
||||
// 良い例 — 早期リターンを伴う複雑なロジックにはループ
|
||||
for user in &users {
|
||||
if let Some(verified) = verify_email(&user.email)? {
|
||||
send_welcome(&verified)?;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## モジュール構成
|
||||
|
||||
型ごとではなく、ドメインごとに整理する:
|
||||
|
||||
```text
|
||||
src/
|
||||
├── main.rs
|
||||
├── lib.rs
|
||||
├── auth/ # ドメインモジュール
|
||||
│ ├── mod.rs
|
||||
│ ├── token.rs
|
||||
│ └── middleware.rs
|
||||
├── orders/ # ドメインモジュール
|
||||
│ ├── mod.rs
|
||||
│ ├── model.rs
|
||||
│ └── service.rs
|
||||
└── db/ # インフラストラクチャ
|
||||
├── mod.rs
|
||||
└── pool.rs
|
||||
```
|
||||
|
||||
## 可視性
|
||||
|
||||
- デフォルトはプライベート。内部共有には `pub(crate)` を使用する
|
||||
- クレートのパブリック API の一部であるものだけに `pub` を付ける
|
||||
- `lib.rs` からパブリック API を再エクスポートする
|
||||
|
||||
## 参考
|
||||
|
||||
包括的な Rust のイディオムとパターンについてはスキル: `rust-patterns` を参照。
|
||||
16
docs/ja-JP/rules/rust/hooks.md
Normal file
16
docs/ja-JP/rules/rust/hooks.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rs"
|
||||
- "**/Cargo.toml"
|
||||
---
|
||||
# Rust フック
|
||||
|
||||
> このファイルは [common/hooks.md](../common/hooks.md) を Rust 固有のコンテンツで拡張します。
|
||||
|
||||
## PostToolUse フック
|
||||
|
||||
`~/.claude/settings.json` で設定する:
|
||||
|
||||
- **cargo fmt**: `.rs` ファイルを編集後に自動フォーマットする
|
||||
- **cargo clippy**: Rust ファイルの編集後にリントチェックを実行する
|
||||
- **cargo check**: 変更後にコンパイルを検証する(`cargo build` よりも高速)
|
||||
168
docs/ja-JP/rules/rust/patterns.md
Normal file
168
docs/ja-JP/rules/rust/patterns.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rs"
|
||||
---
|
||||
# Rust パターン
|
||||
|
||||
> このファイルは [common/patterns.md](../common/patterns.md) を Rust 固有のコンテンツで拡張します。
|
||||
|
||||
## トレイトを使ったリポジトリパターン
|
||||
|
||||
データアクセスをトレイトの背後にカプセル化する:
|
||||
|
||||
```rust
|
||||
pub trait OrderRepository: Send + Sync {
|
||||
fn find_by_id(&self, id: u64) -> Result<Option<Order>, StorageError>;
|
||||
fn find_all(&self) -> Result<Vec<Order>, StorageError>;
|
||||
fn save(&self, order: &Order) -> Result<Order, StorageError>;
|
||||
fn delete(&self, id: u64) -> Result<(), StorageError>;
|
||||
}
|
||||
```
|
||||
|
||||
具象実装がストレージの詳細を処理する(Postgres、SQLite、テスト用インメモリ)。
|
||||
|
||||
## サービスレイヤー
|
||||
|
||||
サービス構造体にビジネスロジックを配置する。コンストラクタ経由で依存関係を注入する:
|
||||
|
||||
```rust
|
||||
pub struct OrderService {
|
||||
repo: Box<dyn OrderRepository>,
|
||||
payment: Box<dyn PaymentGateway>,
|
||||
}
|
||||
|
||||
impl OrderService {
|
||||
pub fn new(repo: Box<dyn OrderRepository>, payment: Box<dyn PaymentGateway>) -> Self {
|
||||
Self { repo, payment }
|
||||
}
|
||||
|
||||
pub fn place_order(&self, request: CreateOrderRequest) -> anyhow::Result<OrderSummary> {
|
||||
let order = Order::from(request);
|
||||
self.payment.charge(order.total())?;
|
||||
let saved = self.repo.save(&order)?;
|
||||
Ok(OrderSummary::from(saved))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 型安全のための Newtype パターン
|
||||
|
||||
引数の取り違えを防ぐために個別のラッパー型を使用する:
|
||||
|
||||
```rust
|
||||
struct UserId(u64);
|
||||
struct OrderId(u64);
|
||||
|
||||
fn get_order(user: UserId, order: OrderId) -> anyhow::Result<Order> {
|
||||
// 呼び出し側で user と order の ID を誤って入れ替えることができない
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
## 列挙型ステートマシン
|
||||
|
||||
状態を列挙型としてモデリングする — 不正な状態を表現不可能にする:
|
||||
|
||||
```rust
|
||||
enum ConnectionState {
|
||||
Disconnected,
|
||||
Connecting { attempt: u32 },
|
||||
Connected { session_id: String },
|
||||
Failed { reason: String, retries: u32 },
|
||||
}
|
||||
|
||||
fn handle(state: &ConnectionState) {
|
||||
match state {
|
||||
ConnectionState::Disconnected => connect(),
|
||||
ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
|
||||
ConnectionState::Connecting { .. } => wait(),
|
||||
ConnectionState::Connected { session_id } => use_session(session_id),
|
||||
ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
|
||||
ConnectionState::Failed { reason, .. } => log_failure(reason),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ビジネス上重要な列挙型では常に網羅的にマッチする — ワイルドカード `_` は使わない。
|
||||
|
||||
## ビルダーパターン
|
||||
|
||||
多数のオプションパラメータを持つ構造体に使用する:
|
||||
|
||||
```rust
|
||||
pub struct ServerConfig {
|
||||
host: String,
|
||||
port: u16,
|
||||
max_connections: usize,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
pub fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
|
||||
ServerConfigBuilder {
|
||||
host: host.into(),
|
||||
port,
|
||||
max_connections: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerConfigBuilder {
|
||||
host: String,
|
||||
port: u16,
|
||||
max_connections: usize,
|
||||
}
|
||||
|
||||
impl ServerConfigBuilder {
|
||||
pub fn max_connections(mut self, n: usize) -> Self {
|
||||
self.max_connections = n;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ServerConfig {
|
||||
ServerConfig {
|
||||
host: self.host,
|
||||
port: self.port,
|
||||
max_connections: self.max_connections,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 拡張性制御のための Sealed トレイト
|
||||
|
||||
プライベートモジュールを使用してトレイトをシールし、外部からの実装を防止する:
|
||||
|
||||
```rust
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub trait Format: private::Sealed {
|
||||
fn encode(&self, data: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
|
||||
pub struct Json;
|
||||
impl private::Sealed for Json {}
|
||||
impl Format for Json {
|
||||
fn encode(&self, data: &[u8]) -> Vec<u8> { todo!() }
|
||||
}
|
||||
```
|
||||
|
||||
## API レスポンスエンベロープ
|
||||
|
||||
ジェネリック列挙型を使用した一貫した API レスポンス:
|
||||
|
||||
```rust
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(tag = "status")]
|
||||
pub enum ApiResponse<T: serde::Serialize> {
|
||||
#[serde(rename = "ok")]
|
||||
Ok { data: T },
|
||||
#[serde(rename = "error")]
|
||||
Error { message: String },
|
||||
}
|
||||
```
|
||||
|
||||
## 参考
|
||||
|
||||
所有権、トレイト、ジェネリクス、並行性、非同期を含む包括的なパターンについてはスキル: `rust-patterns` を参照。
|
||||
141
docs/ja-JP/rules/rust/security.md
Normal file
141
docs/ja-JP/rules/rust/security.md
Normal file
@@ -0,0 +1,141 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rs"
|
||||
---
|
||||
# Rust セキュリティ
|
||||
|
||||
> このファイルは [common/security.md](../common/security.md) を Rust 固有のコンテンツで拡張します。
|
||||
|
||||
## シークレット管理
|
||||
|
||||
- API キー、トークン、資格情報をソースコードにハードコードしない
|
||||
- 環境変数を使用する: `std::env::var("API_KEY")`
|
||||
- 必要なシークレットが起動時に欠落している場合は即座に失敗する
|
||||
- `.env` ファイルは `.gitignore` に含める
|
||||
|
||||
```rust
|
||||
// 悪い例
|
||||
const API_KEY: &str = "sk-abc123...";
|
||||
|
||||
// 良い例 — 早期バリデーション付きの環境変数
|
||||
fn load_api_key() -> anyhow::Result<String> {
|
||||
std::env::var("PAYMENT_API_KEY")
|
||||
.context("PAYMENT_API_KEY must be set")
|
||||
}
|
||||
```
|
||||
|
||||
## SQL インジェクション防止
|
||||
|
||||
- 常にパラメータ化クエリを使用する — ユーザー入力を SQL 文字列にフォーマットしない
|
||||
- バインドパラメータ付きのクエリビルダーまたは ORM(sqlx、diesel、sea-orm)を使用する
|
||||
|
||||
```rust
|
||||
// 悪い例 — フォーマット文字列による SQL インジェクション
|
||||
let query = format!("SELECT * FROM users WHERE name = '{name}'");
|
||||
sqlx::query(&query).fetch_one(&pool).await?;
|
||||
|
||||
// 良い例 — sqlx によるパラメータ化クエリ
|
||||
// プレースホルダ構文はバックエンドにより異なる: Postgres: $1 | MySQL: ? | SQLite: $1
|
||||
sqlx::query("SELECT * FROM users WHERE name = $1")
|
||||
.bind(&name)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
```
|
||||
|
||||
## 入力バリデーション
|
||||
|
||||
- 処理前にシステム境界ですべてのユーザー入力を検証する
|
||||
- 型システムを使用して不変条件を強制する(newtype パターン)
|
||||
- バリデーションではなくパースする — 境界で非構造化データを型付き構造体に変換する
|
||||
- 無効な入力は明確なエラーメッセージで拒否する
|
||||
|
||||
```rust
|
||||
// バリデーションではなくパースする — 無効な状態は表現不可能
|
||||
pub struct Email(String);
|
||||
|
||||
impl Email {
|
||||
pub fn parse(input: &str) -> Result<Self, ValidationError> {
|
||||
let trimmed = input.trim();
|
||||
let at_pos = trimmed.find('@')
|
||||
.filter(|&p| p > 0 && p < trimmed.len() - 1)
|
||||
.ok_or_else(|| ValidationError::InvalidEmail(input.to_string()))?;
|
||||
let domain = &trimmed[at_pos + 1..];
|
||||
if trimmed.len() > 254 || !domain.contains('.') {
|
||||
return Err(ValidationError::InvalidEmail(input.to_string()));
|
||||
}
|
||||
// 本番環境では、バリデーション済みメールクレート(例: `email_address`)の使用を推奨
|
||||
Ok(Self(trimmed.to_string()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## アンセーフコード
|
||||
|
||||
- `unsafe` ブロックを最小限にする — 安全な抽象化を優先する
|
||||
- すべての `unsafe` ブロックには不変条件を説明する `// SAFETY:` コメントが必要
|
||||
- 利便性のためにボローチェッカーを迂回するために `unsafe` を使用しない
|
||||
- レビュー時にすべての `unsafe` コードを監査する — 正当な理由なしに使用するのは危険信号である
|
||||
- C ライブラリには `safe` な FFI ラッパーを優先する
|
||||
|
||||
```rust
|
||||
// 良い例 — safety コメントが必要なすべての不変条件を文書化
|
||||
let widget: &Widget = {
|
||||
// SAFETY: `ptr` は non-null、アライン済み、初期化された Widget を指し、
|
||||
// そのライフタイム中にミュータブル参照やミューテーションは存在しない。
|
||||
unsafe { &*ptr }
|
||||
};
|
||||
|
||||
// 悪い例 — safety の正当化がない
|
||||
unsafe { &*ptr }
|
||||
```
|
||||
|
||||
## 依存関係のセキュリティ
|
||||
|
||||
- `cargo audit` を実行して依存関係の既知の CVE をスキャンする
|
||||
- `cargo deny check` でライセンスとアドバイザリのコンプライアンスを確認する
|
||||
- `cargo tree` で推移的依存関係を監査する
|
||||
- 依存関係を最新に保つ — Dependabot または Renovate を設定する
|
||||
- 依存関係数を最小限にする — 新しいクレートを追加する前に評価する
|
||||
|
||||
```bash
|
||||
# セキュリティ監査
|
||||
cargo audit
|
||||
|
||||
# アドバイザリ、重複バージョン、制限付きライセンスの拒否
|
||||
cargo deny check
|
||||
|
||||
# 依存関係ツリーの検査
|
||||
cargo tree
|
||||
cargo tree -d # 重複のみ表示
|
||||
```
|
||||
|
||||
## エラーメッセージ
|
||||
|
||||
- API レスポンスに内部パス、スタックトレース、データベースエラーを公開しない
|
||||
- 詳細なエラーはサーバー側でログに記録する。クライアントには汎用メッセージを返す
|
||||
- 構造化されたサーバー側ロギングには `tracing` または `log` を使用する
|
||||
|
||||
```rust
|
||||
// エラーを適切なステータスコードと汎用メッセージにマッピングする
|
||||
// (例では axum を使用。レスポンス型はフレームワークに合わせて調整する)
|
||||
match order_service.find_by_id(id) {
|
||||
Ok(order) => Ok((StatusCode::OK, Json(order))),
|
||||
Err(ServiceError::NotFound(_)) => {
|
||||
tracing::info!(order_id = id, "order not found");
|
||||
Err((StatusCode::NOT_FOUND, "Resource not found"))
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(order_id = id, error = %e, "unexpected error");
|
||||
Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 参考
|
||||
|
||||
アンセーフコードのガイドラインと所有権パターンについてはスキル: `rust-patterns` を参照。
|
||||
一般的なセキュリティチェックリストについてはスキル: `security-review` を参照。
|
||||
154
docs/ja-JP/rules/rust/testing.md
Normal file
154
docs/ja-JP/rules/rust/testing.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rs"
|
||||
---
|
||||
# Rust テスト
|
||||
|
||||
> このファイルは [common/testing.md](../common/testing.md) を Rust 固有のコンテンツで拡張します。
|
||||
|
||||
## テストフレームワーク
|
||||
|
||||
- ユニットテストには `#[cfg(test)]` モジュール内の **`#[test]`** を使用する
|
||||
- パラメータ化テストとフィクスチャには **rstest** を使用する
|
||||
- プロパティベーステストには **proptest** を使用する
|
||||
- トレイトベースのモッキングには **mockall** を使用する
|
||||
- 非同期テストには **`#[tokio::test]`** を使用する
|
||||
|
||||
## テストの構成
|
||||
|
||||
```text
|
||||
my_crate/
|
||||
├── src/
|
||||
│ ├── lib.rs # #[cfg(test)] モジュール内のユニットテスト
|
||||
│ ├── auth/
|
||||
│ │ └── mod.rs # #[cfg(test)] mod tests { ... }
|
||||
│ └── orders/
|
||||
│ └── service.rs # #[cfg(test)] mod tests { ... }
|
||||
├── tests/ # 統合テスト(各ファイル = 個別のバイナリ)
|
||||
│ ├── api_test.rs
|
||||
│ ├── db_test.rs
|
||||
│ └── common/ # 共有テストユーティリティ
|
||||
│ └── mod.rs
|
||||
└── benches/ # Criterion ベンチマーク
|
||||
└── benchmark.rs
|
||||
```
|
||||
|
||||
ユニットテストは同じファイル内の `#[cfg(test)]` モジュールに配置する。統合テストは `tests/` に配置する。
|
||||
|
||||
## ユニットテストのパターン
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn creates_user_with_valid_email() {
|
||||
let user = User::new("Alice", "alice@example.com").unwrap();
|
||||
assert_eq!(user.name, "Alice");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_email() {
|
||||
let result = User::new("Bob", "not-an-email");
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().to_string().contains("invalid email"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## パラメータ化テスト
|
||||
|
||||
```rust
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case("hello", 5)]
|
||||
#[case("", 0)]
|
||||
#[case("rust", 4)]
|
||||
fn test_string_length(#[case] input: &str, #[case] expected: usize) {
|
||||
assert_eq!(input.len(), expected);
|
||||
}
|
||||
```
|
||||
|
||||
## 非同期テスト
|
||||
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn fetches_data_successfully() {
|
||||
let client = TestClient::new().await;
|
||||
let result = client.get("/data").await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
```
|
||||
|
||||
## mockall によるモッキング
|
||||
|
||||
本番コードでトレイトを定義し、テストモジュールでモックを生成する:
|
||||
|
||||
```rust
|
||||
// 本番トレイト — 統合テストがインポートできるように pub にする
|
||||
pub trait UserRepository {
|
||||
fn find_by_id(&self, id: u64) -> Option<User>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use mockall::predicate::eq;
|
||||
|
||||
mockall::mock! {
|
||||
pub Repo {}
|
||||
impl UserRepository for Repo {
|
||||
fn find_by_id(&self, id: u64) -> Option<User>;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_returns_user_when_found() {
|
||||
let mut mock = MockRepo::new();
|
||||
mock.expect_find_by_id()
|
||||
.with(eq(42))
|
||||
.times(1)
|
||||
.returning(|_| Some(User { id: 42, name: "Alice".into() }));
|
||||
|
||||
let service = UserService::new(Box::new(mock));
|
||||
let user = service.get_user(42).unwrap();
|
||||
assert_eq!(user.name, "Alice");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## テストの命名
|
||||
|
||||
シナリオを説明する記述的な名前を使用する:
|
||||
- `creates_user_with_valid_email()`
|
||||
- `rejects_order_when_insufficient_stock()`
|
||||
- `returns_none_when_not_found()`
|
||||
|
||||
## カバレッジ
|
||||
|
||||
- 80%以上の行カバレッジを目標にする
|
||||
- カバレッジレポートには **cargo-llvm-cov** を使用する
|
||||
- ビジネスロジックに集中する — 生成コードと FFI バインディングは除外する
|
||||
|
||||
```bash
|
||||
cargo llvm-cov # サマリー
|
||||
cargo llvm-cov --html # HTML レポート
|
||||
cargo llvm-cov --fail-under-lines 80 # しきい値以下で失敗
|
||||
```
|
||||
|
||||
## テストコマンド
|
||||
|
||||
```bash
|
||||
cargo test # すべてのテストを実行
|
||||
cargo test -- --nocapture # println 出力を表示
|
||||
cargo test test_name # パターンに一致するテストを実行
|
||||
cargo test --lib # ユニットテストのみ
|
||||
cargo test --test api_test # 特定の統合テスト(tests/api_test.rs)
|
||||
cargo test --doc # ドキュメントテストのみ
|
||||
```
|
||||
|
||||
## 参考
|
||||
|
||||
プロパティベーステスト、フィクスチャ、Criterion によるベンチマークを含む包括的なテストパターンについてはスキル: `rust-testing` を参照。
|
||||
Reference in New Issue
Block a user