--- description: This is designed to help understand how to do testing in this project. globs: src/* --- # Testing Rules and Best Practices ## General Testing Guidelines - All tests must be async and use tokio test framework - Tests should be well-documented with clear test case descriptions and expected outputs - Each test should focus on testing a single piece of functionality - Tests should be independent and not rely on the state of other tests - Use meaningful test names that describe what is being tested ## Unit Tests - Unit tests should be inline with the code they are testing using `#[cfg(test)]` modules - Each public function should have corresponding unit tests - Mock external dependencies using mockito for HTTP calls - Use `mockito::Server::new_async()` instead of `mockito::Server::new()` - Test both success and error cases - Test edge cases and boundary conditions ## Integration Tests - Integration tests should be placed in the `/tests` directory - Organize integration tests to mirror the main codebase structure - Each major feature/resource should have its own test file - Test the interaction between multiple components - Use real dependencies when possible, mock only what's necessary - Include end-to-end workflow tests ## Test Structure ```rust #[cfg(test)] mod tests { use super::*; use mockito; use tokio; // Optional: Setup function for common test initialization async fn setup() -> TestContext { // Setup code here } #[tokio::test] async fn test_name() { // Test case description in comments // Expected output in comments // Arrange // Setup test data and dependencies // Act // Execute the function being tested // Assert // Verify the results } } ``` ## Mocking Guidelines - Use mockito for HTTP service mocks - Create mock responses that match real API responses - Include both successful and error responses in mocks - Clean up mocks after tests complete ## Error Testing - Test error conditions and error handling - Verify error messages and error types - Test timeout scenarios - Test connection failures - Test invalid input handling ## Database Testing - Use a separate test database for integration tests - Clean up test data after tests complete - Test database transactions and rollbacks - Test database connection error handling ## WebSocket Testing - Test WebSocket connection establishment - Test message sending and receiving - Test connection closure handling - Test reconnection logic - Test concurrent WebSocket connections ## Performance Testing - Include basic performance tests for critical paths - Test response times under normal conditions - Test handling of concurrent requests - Test resource cleanup ## Test Output - Tests should provide clear error messages - Use descriptive assert messages - Print relevant debug information in test failures - Log test execution progress for long-running tests ## CI/CD Considerations - All tests must pass in CI environment - Tests should be reproducible - Tests should not have external dependencies that could fail CI - Test execution time should be reasonable ## Example Test ```rust #[cfg(test)] mod tests { use super::*; use mockito; use tokio; #[tokio::test] async fn test_api_call_success() { // Test case: Successful API call returns expected response // Expected: Response contains user data with status 200 let mut server = mockito::Server::new_async().await; let mock = server .mock("GET", "/api/user") .match_header("authorization", "Bearer test-token") .with_status(200) .with_body(r#"{"id": "123", "name": "Test User"}"#) .create(); let client = ApiClient::new(server.url()); let response = client.get_user().await.unwrap(); assert_eq!(response.id, "123"); assert_eq!(response.name, "Test User"); mock.assert(); } } ```