### Run the Workshop Runner CLI Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md After installing the 'wr' CLI, navigate to the repository's top-level folder and run this command to start the course and verify solutions. ```bash wr ``` -------------------------------- ### Start MockServer and Get Base URL Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/07_http_mocking/01_basics.md Demonstrates how to start a MockServer instance and retrieve its base URL. The server is launched on a random port to allow parallel test execution. ```rust use wiremock::MockServer; #[tokio::test] async fn test() { let mock_server = MockServer::start().await; let base_url = mock_server.uri(); // ... } ``` -------------------------------- ### Install cargo-insta CLI Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/02_snapshots/00_intro.md Install the 'cargo-insta' CLI tool, which is used to manage snapshots. ```bash cargo install --locked cargo-insta ``` -------------------------------- ### Install Rust Nightly Toolchain Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md Installs the `nightly` Rust toolchain, which is required for the course. ```bash rustup toolchain install nightly ``` -------------------------------- ### Check Rustup Installation Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/README.md Verify that `rustup` is installed on your system. `rustup` is the Rust toolchain installer and manager, and it's a prerequisite for managing your Rust environment. ```bash rustup --version ``` -------------------------------- ### Mount a Basic GET Request Mock Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/07_http_mocking/01_basics.md Configures a MockServer to respond to GET requests with a 200 OK status. This mock is mounted to the server and is expected to be called at least once. ```rust use wiremock::{MockServer, Mock, ResponseTemplate}; use wiremock::matchers::method; #[tokio::test] async fn test() { let mock_server = MockServer::start().await; // Precondition: do what follows only if the request method is "GET" Mock::given(method("GET")) // Response value: return a 200 OK .respond_with(ResponseTemplate::new(200)) // Expectation: panic if this mock doesn't match at least once .expect(1..) .mount(&mock_server) .await; // [...] ``` -------------------------------- ### Run PostgreSQL Docker Container Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/06_database_isolation/01_sqlx_test.md Starts a PostgreSQL 15 Docker container, exposing port 5432 and setting up user, password, and database. ```bash docker run -p 5432:5432 \ -e POSTGRES_PASSWORD=password \ -e POSTGRES_USER=postgres \ -e POSTGRES_DB=postgres \ --name test_db \ postgres:15 ``` -------------------------------- ### Original Function to Get Latest Release Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/07_http_mocking/01_basics.md An example function that fetches the latest release tag for a GitHub repository using reqwest. This function has a hard-coded base URL and is not directly testable with wiremock. ```rust use reqwest::Client; async fn get_latest_release(client: &Client, repo: &str) -> Result { let url = format!("https://api.github.com/repos/{repo}/releases/latest"); let response = client.get(&url).send().await?; let release = response.json::().await?; let tag = release["tag_name"].as_str().unwrap(); Ok(tag.into()) } ``` -------------------------------- ### Inline Snapshot Example Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/02_snapshots/02_storage_location.md Demonstrates the usage of inline snapshots, where the expected snapshot value is stored directly within the test code. This method is convenient for smaller snapshots but can clutter tests with larger data. ```rust #[test] fn snapshot() { let m = "The new value I want to save"; assert_snapshot!(m, @"The old snapshot I want to compare against") } ``` -------------------------------- ### External Snapshot Example Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/02_snapshots/02_storage_location.md Shows how to use external snapshots, where the snapshot data is stored in a separate file, keeping test files cleaner. By default, these are stored in a 'snapshots' directory. ```rust #[test] fn snapshot() { let m = "The new value I want to save"; assert_snapshot!(m) } ``` -------------------------------- ### Usage of `#[tokio::test]` with Arguments Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/08_macros/00_intro.md An example of using the `#[tokio::test]` macro with a specific flavor argument. This macro is used to enable asynchronous tests in Rust. ```rust #[tokio::test(flavor = "multi_thread")] async fn it_works() { assert!(true); } ``` -------------------------------- ### Update Rust to Latest Stable Toolchain Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md Use this command if Rust is already installed via `rustup` to update to the latest stable toolchain. ```bash # If you installed Rust using `rustup`, the recommended way, you can update to the latest stable toolchain with: rustup update stable ``` -------------------------------- ### Get Temporary Directory Path Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/05_filesystem_isolation/02_tempfile.md Retrieves the path to the operating system's temporary directory. Files created here are eventually deleted by the OS. ```rust use std::env; let temp_dir = env::temp_dir(); println!("The temporary directory is: {:?}", temp_dir); ``` -------------------------------- ### Test Repository Query with Checkpoints Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/04_checkpoints.md Demonstrates setting up a repository, defining expectations for `Repository::query`, and using a checkpoint to verify expectations for `Repository::new` before proceeding. ```rust #[test] fn test_repository_query() { let mut mock = MockUserProvider::new(); let mut repo = setup_repository(&mut mock); // Set up expectations for Repository::query // [...] // Call Repository::query // [...] } ``` ```rust fn setup_repository(mock: &mut MockUserProvider) -> Repository { // Arrange mock.expect_is_authenticated() .returning(|_| true); // [...] // Act let repository = Repository::new(mock); // Verify that all expectations up to the checkpoint have been met mock.checkpoint(); repository } ``` -------------------------------- ### Create a New Branch for Solutions Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md Navigate to the cloned repository and create a new branch named 'my-solutions' to track your progress. ```bash cd rust-advanced-testing-workshop git checkout -b my-solutions ``` -------------------------------- ### Basic Assertions with assert!, assert_eq!, assert_ne! Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/01_better_assertions/01_std_assertions.md Demonstrates the basic usage of assert!, assert_eq!, and assert_ne! macros within a test function. These are fundamental for verifying conditions and values during testing. ```rust #[test] fn t() { assert!(true); assert_eq!(1, 1); assert_ne!(1, 2); } ``` -------------------------------- ### Centralizing Hardcoded Paths Near Entrypoint Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/05_filesystem_isolation/01_named_tempfile.md Demonstrates placing hardcoded paths near the binary's entrypoint (e.g., `main` function) to limit the scope of code with implicit filesystem dependencies. ```rust use std::path::PathBuf; use crate::get_cli_path; fn main() { let config_path = PathBuf::from("config.txt"); let cli_path = get_cli_path(&config_path); } ``` -------------------------------- ### Clone the Workshop Repository (HTTPS) Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md Clone the companion GitHub repository using HTTPS. Use this if you do not have an SSH key set up with GitHub. ```bash # Otherwise, use the HTTPS URL: # # git clone https://github.com/mainmatter/rust-advanced-testing-workshop.git ``` -------------------------------- ### Open Rust Documentation Offline Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/README.md Access the Rust standard library documentation directly from your local machine. This is useful for quick lookups without an internet connection. ```bash rustup doc ``` -------------------------------- ### Clone the Workshop Repository (SSH) Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/00_intro/00_welcome.md Clone the companion GitHub repository using SSH. Ensure you have an SSH key set up with GitHub. ```bash # If you have an SSH key set up with GitHub git clone git@github.com:mainmatter/rust-advanced-testing-workshop.git ``` -------------------------------- ### Implicit Filesystem Interaction in Rust Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/05_filesystem_isolation/01_named_tempfile.md This function implicitly opens and reads from a hardcoded 'config.txt' file. Its dependency on the filesystem is not obvious from the signature, making it harder to test. ```rust use std::io::{BufReader, BufRead}; use std::path::PathBuf; fn get_cli_path() -> PathBuf { let config = std::fs::File::open("config.txt").unwrap(); let reader = BufReader::new(config); let path = reader.lines().next().unwrap().unwrap(); PathBuf::from(path) } ``` -------------------------------- ### Repository Structure and Methods Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/04_checkpoints.md Defines the `Repository` struct and its associated methods, `new` and `query`, which depend on a `UserProvider` trait. ```rust pub struct Repository(/* ... */); impl Repository { pub fn new(up: T) -> Self { // ... } pub fn query(&self, id: u32, up: T) -> Option { // ... } } ``` -------------------------------- ### Defining Method Call Sequences with `Sequence` Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/03_sequences.md Illustrates how to enforce a specific order of method calls on mock objects using `Sequence`. The test will fail if methods are not called in the defined sequence. ```rust #[test] fn test_sequence() { let mut mock = MockEmailSender::new(); let email = /* */; let mut sequence = Sequence::new(); mock.expect_send() .times(1) .in_sequence(&mut sequence) .returning(|_| Ok(())); mock.expect_get_inbox() .times(1) .in_sequence(&mut sequence) .returning(|_| Ok(/* */)); // This panics because the sequence expected `send` to be called first! mock.get_inbox(); } ``` -------------------------------- ### Cargo.toml Configuration for Procedural Macros Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/08_macros/01_no_op_macro.md This configuration is required in the `Cargo.toml` file of a crate intended to contain procedural macros. It signals to Cargo that the crate should be compiled as a procedural macro crate. ```toml [lib] proc-macro = true ``` -------------------------------- ### Custom Panic Message with assert_eq! Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/01_better_assertions/01_std_assertions.md Demonstrates how to provide a custom panic message to the assert_eq! macro. This message is printed in addition to the default comparison output when the assertion fails, offering more context. ```rust #[test] fn assertion_with_message() { assert_eq!(2 + 2, 5, "The Rust compiler hasn't read 1984 by George Orwell.") } ``` -------------------------------- ### Basic `#[tokio::test]` Macro Definition Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/08_macros/00_intro.md This shows the basic structure of a procedural attribute macro for `#[tokio::test]`. It accepts token streams for arguments and the annotated item, returning a transformed token stream. ```rust use proc_macro::TokenStream; #[proc_macro_attribute] pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { // [...] ``` -------------------------------- ### External Snapshot with Custom Name Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/02_snapshots/02_storage_location.md Illustrates how to specify a custom name for an external snapshot file. This allows for more organized or descriptive naming conventions beyond the default derived name. ```rust #[test] fn snapshot() { let m = "The new value I want to save"; assert_snapshot!("custom_snapshot_name", m) } ``` -------------------------------- ### Original Login Function Signature Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/01_traits.md The initial signature of the login function before refactoring, showing concrete dependency types. ```rust async fn login( request: &HttpRequest, database_pool: &DatabasePool, auth0_client: &Auth0Client, rate_limiter: &RateLimiter, ) -> Result { // [...] } ``` -------------------------------- ### Refactored Function Accepting Path Argument Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/05_filesystem_isolation/01_named_tempfile.md Refactors the previous function to accept the configuration file path as an argument, making the filesystem dependency explicit and improving testability. ```rust use std::io::{BufReader, BufRead}; use std::path::{PathBuf, Path}; fn get_cli_path(config_path: &Path) -> PathBuf { let config = std::fs::File::open(config_path).unwrap(); let reader = BufReader::new(config); let path = reader.lines().next().unwrap().unwrap(); PathBuf::from(path) } ``` -------------------------------- ### Configure mock behavior in a test Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/02_mockall.md Configure the mock object's behavior, including preconditions, expected call counts, and return values, before the method is called. ```rust #[test] fn test_email_sender() { let mut mock = MockEmailSender::new(); mock.expect_send() // Precondition: do what follows only if the email subject is "Hello" .withf(|email| email.subject == "Hello") // Expectation: panic if the method is not called exactly once .times(1) // Return value .returning(|_| Ok(())); // [...] } ``` -------------------------------- ### Abstracted Configuration Reading Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/05_filesystem_isolation/02_tempfile.md Refactored function to read a path from a `BufRead` source, abstracting away direct filesystem access. This makes the function testable with in-memory data structures. ```rust use std::io::BufRead; use std::path::PathBuf; fn get_cli_path(config: R) -> PathBuf where R: BufRead, { let path = config .lines() .next() .expect("The config is empty") .expect("First line is not valid UTF-8"); PathBuf::from(path) } ``` -------------------------------- ### Enable automatic mocking with `#[automock]` Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/02_mockall.md Apply the `#[automock]` attribute to a trait to generate a mock implementation. This requires importing `mockall::automock`. ```rust use mockall::automock; #[automock] pub trait EmailSender { fn send(&self, email: &Email) -> Result<(), EmailError>; } ``` -------------------------------- ### Mocking Multiple Calls with `times` Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/03_sequences.md Demonstrates how to set an expectation for a specific number of calls to a mock method. Exceeding the specified `times` will cause a panic. ```rust #[test] fn test_times() { let mut mock = MockEmailSender::new(); let email = /* */; mock.expect_send() .times(2) .returning(|_| Ok(())); mock.send(&email); mock.send(&email); // This panics! mock.send(&email); } ``` -------------------------------- ### Refactored Function to Accept Base URL Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/07_http_mocking/01_basics.md A modified version of the get_latest_release function that accepts the GitHub base URI as an argument. This allows the function to be tested with a configurable MockServer. ```rust use reqwest::Client; async fn get_latest_release(client: &Client, github_base_uri: http::Uri, repo: &str) -> Result { let endpoint = format!("{github_base_uri}/repos/{repo}/releases/latest"); let response = client.get(&endpoint).send().await?; let release = response.json::().await?; let tag = release["tag_name"].as_str().unwrap(); Ok(tag.into()) } ``` -------------------------------- ### Transformed `#[tokio::test]` Code Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/08_macros/00_intro.md This illustrates the output of the `#[tokio::test]` macro. It transforms an async test function into a synchronous one that sets up and runs a Tokio runtime. ```rust #[test] fn it_works() { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() .block_on(async { assert!(true); }) } ``` -------------------------------- ### Declare a Test Target Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/09_test_harness/00_intro.md Declare a custom test target named 'integration' in your `Cargo.toml` file. Cargo will look for the test entrypoint in `tests/integration.rs` by default. ```toml [[test]] name = "integration" ``` -------------------------------- ### Update Rust Toolchain Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/README.md Ensure you are running the latest compiler version by updating your Rust toolchain. This command is essential for compatibility and accessing the newest features. ```bash rustup update ``` -------------------------------- ### Returning Different Values on Multiple Calls Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/03_sequences.md Shows how to configure a mock method to return different results based on the invocation count, using separate `times` expectations. ```rust #[test] fn test_times() { let mut mock = MockEmailSender::new(); let email = /* */; mock.expect_send() .times(1) .returning(|_| Ok(())); mock.expect_send() .times(1) .returning(|_| Err(/* */)); // This returns Ok(())... mock.send(&email); // ...while this returns Err! mock.send(&email); } ``` -------------------------------- ### Implement Authenticator Trait for Auth0Client Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/01_traits.md Provides a concrete implementation of the Authenticator trait for the Auth0Client type. ```rust impl Authenticator for Auth0Client { async fn verify(&self, jwt: &str) -> Result { // [...] } } ``` -------------------------------- ### Login Endpoint Function Signature Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/00_intro.md This Rust function signature for a login endpoint shows its dependencies: HttpRequest, DatabasePool, Auth0Client, and RateLimiter. These dependencies must be provided when invoking the function in tests. ```rust async fn login( request: &HttpRequest, database_pool: &DatabasePool, auth0_client: &Auth0Client, rate_limiter: &RateLimiter, ) -> Result { // [...] ``` -------------------------------- ### Implementing a Custom Matcher in Rust Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/07_http_mocking/02_match.md Implement the `Match` trait to create custom request matchers for WireMock. This involves defining the `matches` method which returns a boolean indicating if the request meets the matching criteria. ```rust pub trait Match: Send + Sync { // Required method fn matches(&self, request: &Request) -> bool; } ``` -------------------------------- ### Basic Attribute Procedural Macro Structure Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/08_macros/01_no_op_macro.md This Rust code defines the basic structure of an attribute procedural macro. It uses the `#[proc_macro_attribute]` annotation and specifies the function signature, which accepts two `TokenStream` arguments and returns a `TokenStream`. ```rust use proc_macro::TokenStream; #[proc_macro_attribute] pub fn my_attribute_macro(args: TokenStream, item: TokenStream) -> TokenStream { // [...] ``` -------------------------------- ### assert_eq! Failure with Default Message Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/01_better_assertions/01_std_assertions.md Shows a test case where assert_eq! fails, illustrating the default panic message that includes the left and right values being compared. This is useful for debugging equality checks. ```rust #[test] fn t() { assert_eq!(1, 2); } ``` -------------------------------- ### Define a trait for mocking Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/02_mockall.md This is the original trait definition before applying the `#[automock]` attribute. ```rust pub trait EmailSender { fn send(&self, email: &Email) -> Result<(), EmailError>; } ``` -------------------------------- ### assert! Failure with Default Message Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/01_better_assertions/01_std_assertions.md Illustrates a test case where assert! fails, showing the default panic message which includes the stringified condition that was checked. This helps in understanding why a boolean condition evaluated to false. ```rust #[test] fn t() { let v = vec![1]; assert!(v.is_empty()); } ``` -------------------------------- ### Define an Authenticator Trait Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/01_traits.md Defines a trait that abstracts the necessary authentication functionality, decoupling it from a specific implementation. ```rust trait Authenticator { async fn verify(&self, jwt: &str) -> Result; } ``` -------------------------------- ### Refactored Login Function Signature with Trait Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/03_mocks/01_traits.md The login function signature after refactoring, now accepting a generic type that implements the Authenticator trait. ```rust async fn login( request: &HttpRequest, database_pool: &DatabasePool, authenticator: &A, rate_limiter: &RateLimiter, ) -> Result where A: Authenticator, { // [...] } ``` -------------------------------- ### Disable Default Test Harness Source: https://github.com/mainmatter/rust-advanced-testing-workshop/blob/main/book/src/09_test_harness/00_intro.md Configure a test target to disable the default Rust test harness. This requires you to provide your own entrypoint for running tests. ```toml [[test]] name = "integration" harness = false ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.