--- description: Create integration tests in the tests/ directory --- You are helping create integration tests for a Rust project in the tests/ directory. ## Your Task Set up integration test infrastructure and create comprehensive integration tests that test the public API. ## Steps 1. **Ask for Test Details** Ask the user: - What feature/module to integration test? - Do you need database/HTTP mocking? - Should we use testcontainers for real databases? 2. **Create Integration Test File** Create a new file in `tests/` directory: ``` tests/ ├── [feature]_integration.rs └── common/ └── mod.rs # Shared test utilities ``` 3. **Set Up Common Utilities** Create `tests/common/mod.rs`: ```rust // tests/common/mod.rs #![allow(dead_code)] use my_crate::*; pub fn setup() { // Common setup logic } pub fn teardown() { // Common cleanup logic } ``` 4. **Generate Integration Test** Create the integration test file: ```rust // tests/[feature]_integration.rs use my_crate::*; mod common; #[test] fn test_[feature]_complete_workflow() { common::setup(); // Arrange let app = create_test_application(); // Act let result = app.execute_workflow(); // Assert assert!(result.is_ok()); common::teardown(); } fn create_test_application() -> Application { // Setup test instance with test configuration Application::new(test_config()) } fn test_config() -> Config { Config { database_url: "postgres://localhost/test_db".to_string(), // ... other test config } } ``` 5. **Add Async Integration Tests** For async applications: ```rust #[tokio::test] async fn test_async_integration() { let app = setup_test_app().await; let result = app.process_request(test_request()).await; assert!(result.is_ok()); } async fn setup_test_app() -> Application { Application::new(test_config()).await.unwrap() } ``` 6. **Add Database Integration Tests** Using testcontainers: ```rust use testcontainers::{clients, images::postgres::Postgres}; use sqlx::PgPool; #[tokio::test] async fn test_with_real_database() { let docker = clients::Cli::default(); let postgres = docker.run(Postgres::default()); let connection_string = format!( "postgres://postgres@localhost:{}/postgres", postgres.get_host_port_ipv4(5432) ); let pool = PgPool::connect(&connection_string).await.unwrap(); // Run migrations sqlx::migrate!("./migrations") .run(&pool) .await .unwrap(); // Now test with real database let repo = PostgresRepository::new(pool); let service = MyService::new(repo); let result = service.create_item("test").await; assert!(result.is_ok()); } ``` Or create a helper in common: ```rust // tests/common/mod.rs use sqlx::PgPool; use testcontainers::*; pub async fn setup_test_database() -> PgPool { let docker = clients::Cli::default(); let postgres = docker.run(images::postgres::Postgres::default()); let url = format!( "postgres://postgres@localhost:{}/postgres", postgres.get_host_port_ipv4(5432) ); let pool = PgPool::connect(&url).await.unwrap(); sqlx::migrate!().run(&pool).await.unwrap(); pool } // tests/database_integration.rs mod common; #[tokio::test] async fn test_repository() { let pool = common::setup_test_database().await; let repo = MyRepository::new(pool); // Test repository operations } ``` 7. **Add HTTP API Integration Tests** For testing HTTP APIs: ```rust use axum::body::Body; use axum::http::{Request, StatusCode}; use tower::ServiceExt; #[tokio::test] async fn test_api_endpoint() { let app = create_test_app(); let response = app .oneshot( Request::builder() .uri("/api/users") .method("GET") .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::OK); let body = hyper::body::to_bytes(response.into_body()) .await .unwrap(); let users: Vec = serde_json::from_slice(&body).unwrap(); assert!(!users.is_empty()); } ``` Or using reqwest for full HTTP testing: ```rust #[tokio::test] async fn test_api_full_request() { let server = spawn_test_server().await; let client = reqwest::Client::new(); let response = client .get(format!("{}/api/users", server.url())) .send() .await .unwrap(); assert_eq!(response.status(), 200); let users: Vec = response.json().await.unwrap(); assert!(!users.is_empty()); } async fn spawn_test_server() -> TestServer { // Start server on random port for testing TestServer::spawn().await } ``` 8. **Add HTTP Mocking with wiremock** For testing external API calls: ```rust use wiremock::{MockServer, Mock, ResponseTemplate}; use wiremock::matchers::{method, path}; #[tokio::test] async fn test_external_api_integration() { let mock_server = MockServer::start().await; Mock::given(method("GET")) .and(path("/external/api")) .respond_with(ResponseTemplate::new(200).set_body_json( serde_json::json!({ "status": "ok", "data": "test" }) )) .mount(&mock_server) .await; let client = ExternalApiClient::new(&mock_server.uri()); let result = client.fetch_data().await.unwrap(); assert_eq!(result.status, "ok"); } ``` 9. **Add Multi-Step Workflow Tests** Test complete user workflows: ```rust #[tokio::test] async fn test_complete_user_workflow() { let app = setup_test_app().await; // Step 1: Create user let user_id = app .create_user("test@example.com") .await .unwrap(); // Step 2: Retrieve user let user = app .get_user(&user_id) .await .unwrap(); assert_eq!(user.email, "test@example.com"); // Step 3: Update user app.update_user(&user_id, "new@example.com") .await .unwrap(); // Step 4: Verify update let updated = app .get_user(&user_id) .await .unwrap(); assert_eq!(updated.email, "new@example.com"); // Step 5: Delete user app.delete_user(&user_id) .await .unwrap(); // Step 6: Verify deletion let result = app.get_user(&user_id).await; assert!(result.is_err()); } ``` 10. **Update Dev Dependencies** Ensure required dependencies are in Cargo.toml: ```toml [dev-dependencies] tokio = { version = "1", features = ["full", "test-util"] } testcontainers = "0.15" wiremock = "0.6" reqwest = { version = "0.11", features = ["json"] } ``` 11. **Provide Summary** ``` ✅ Integration tests created successfully! ## Files Created: - `tests/[feature]_integration.rs` - Main integration test file - `tests/common/mod.rs` - Shared test utilities ## Tests Added: - test_[feature]_complete_workflow - test_database_operations - test_api_endpoints - test_external_api_integration ## Infrastructure: - Database test setup with testcontainers - HTTP mocking with wiremock - Test configuration helpers ## Dependencies Added: [List of dev dependencies] ## Running Integration Tests: ```bash # Run all integration tests cargo test --test [feature]_integration # Run specific test cargo test test_complete_workflow # Run with output cargo test --test [feature]_integration -- --nocapture # Run all integration tests cargo test --tests ``` ## Next Steps: 1. Review and customize test cases 2. Add more workflow scenarios 3. Run tests: `cargo test --tests` 4. Check if you need more test infrastructure ``` ## Integration Test Patterns ### Setup/Teardown Pattern ```rust struct TestContext { pool: PgPool, // Other resources } impl TestContext { async fn new() -> Self { // Setup Self { pool: setup_database().await, } } } impl Drop for TestContext { fn drop(&mut self) { // Cleanup } } #[tokio::test] async fn test_with_context() { let ctx = TestContext::new().await; // Use ctx.pool } ``` ### Parallel Test Isolation ```rust #[tokio::test] async fn test_isolated_1() { let db = create_unique_test_db("test1").await; // Each test gets its own database } #[tokio::test] async fn test_isolated_2() { let db = create_unique_test_db("test2").await; // Runs in parallel without interference } ``` ## Important Notes - Integration tests are in separate crates from src/ - Each file in tests/ is a separate binary - Common code goes in tests/common/ (not tests/common.rs) - Use real implementations when possible - Mock external services - Clean up resources after tests - Tests should be independent and runnable in any order ## After Completion Ask the user: 1. Did the integration tests pass? 2. Do you need more test scenarios? 3. Should we add performance/load tests? 4. Do you want to set up CI/CD for these tests?