Crate aws_smithy_mocks
source ·Expand description
§aws-smithy-mocks
A flexible mocking framework for testing clients generated by smithy-rs, including all packages of the AWS SDK for Rust.
This crate provides a simple yet powerful way to mock SDK client responses for testing purposes. It uses interceptors to return stub responses, allowing you to test both happy-path and error scenarios without mocking the entire client or using traits.
§Key Features
- Simple API: Create mock rules with a fluent API using the
mock!
macro - Flexible Response Types: Return modeled outputs, errors, or raw HTTP responses
- Request Matching: Match requests based on their properties
- Response Sequencing: Define sequences of responses for testing retry behavior
- Rule Modes: Control how rules are matched and applied
§Basic Usage
use aws_sdk_s3::operation::get_object::GetObjectOutput;
use aws_sdk_s3::Client;
use aws_smithy_types::byte_stream::ByteStream;
use aws_smithy_mocks::{mock, mock_client};
#[tokio::test]
async fn test_s3_get_object() {
// Create a rule that returns a successful response
let get_object_rule = mock!(Client::get_object)
.match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("test-key"))
.then_output(|| GetObjectOutput::builder()
.body(ByteStream::from_static(b"test-content"))
.build()
);
// Create a mocked client with the rule
let s3 = mock_client!(aws_sdk_s3, [&get_object_rule]);
// Use the client as you would normally
let result = s3.get_object()
.bucket("test-bucket")
.key("test-key")
.send()
.await
.expect("success response");
// Verify the response
let data = result.body.collect().await.expect("successful read").to_vec();
assert_eq!(data, b"test-content");
// Verify the rule was used
assert_eq!(get_object_rule.num_calls(), 1);
}
§Creating Rules
Rules are created using the mock!
macro, which takes a client operation as an argument:
let rule = mock!(Client::get_object)
// Optional: Add a matcher to filter requests
.match_requests(|req| req.bucket() == Some("test-bucket"))
// Add a response
.then_output(|| GetObjectOutput::builder().build());
§Response Types
You can return different types of responses:
// Return a modeled output
let success_rule = mock!(Client::get_object)
.then_output(|| GetObjectOutput::builder().build());
// Return a modeled error
let error_rule = mock!(Client::get_object)
.then_error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()));
// Return an HTTP response
let http_rule = mock!(Client::get_object)
.then_http_response(|| HttpResponse::new(
StatusCode::try_from(503).unwrap(),
SdkBody::from("service unavailable")
));
§Response Sequences
For testing retry behavior or complex scenarios, you can define sequences of responses using the sequence builder API:
let retry_rule = mock!(Client::get_object)
.sequence()
.http_status(503, None) // First call returns 503
.http_status(503, None) // Second call returns 503
.output(|| GetObjectOutput::builder().build()) // Third call succeeds
.build();
// With repetition using `times()`
let retry_rule = mock!(Client::get_object)
.sequence()
.http_status(503, None)
.times(2) // First two calls return 503
.output(|| GetObjectOutput::builder().build()) // Third call succeeds
.build();
The sequence builder API provides a fluent interface for defining sequences of responses. After providing all responses in the sequence, the rule is considered exhausted.
§Creating Mocked Clients
Use the mock_client!
macro to create a client with your rules:
// Create a client with a single rule
let client = mock_client!(aws_sdk_s3, [&rule]);
// Create a client with multiple rules and a specific rule mode
let client = mock_client!(aws_sdk_s3, RuleMode::Sequential, [&rule1, &rule2]);
// Create a client with additional configuration
let client = mock_client!(
aws_sdk_s3,
RuleMode::Sequential,
[&rule],
|config| config.force_path_style(true)
);
§Rule Modes
The RuleMode
enum controls how rules are matched and applied:
RuleMode::Sequential
: Rules are tried in order. When a rule is exhausted, the next rule is used.RuleMode::MatchAny
: The first matching rule is used, regardless of order.
let interceptor = MockResponseInterceptor::new()
.rule_mode(RuleMode::Sequential)
.with_rule(&rule1)
.with_rule(&rule2);
§Testing Retry Behavior
The mocking framework supports testing retry behavior by allowing you to define sequences of responses:
#[tokio::test]
async fn test_retry() {
// Create a rule that returns errors for the first two attempts, then succeeds
let rule = mock!(Client::get_object)
.sequence()
.http_status(503, None)
.times(2) // Service unavailable for first two calls
.output(|| GetObjectOutput::builder().build()) // Success on third call
.build();
// Create a client with retry enabled
let client = mock_client!(aws_sdk_s3, [&rule]);
// The operation should succeed after retries
let result = client.get_object()
.bucket("test-bucket")
.key("test-key")
.send()
.await;
assert!(result.is_ok());
assert_eq!(rule.num_calls(), 3); // Called 3 times (2 failures + 1 success)
}
Macros§
mock!
macro that produces aRuleBuilder
from a client invocationmock_client!
macro produces a Client configured with a number of Rules and appropriate test default configuration.
Structs§
- Interceptor which produces mock responses based on a list of rules
- A rule for matching requests and providing mock responses.
- A builder for creating rules.
Enums§
- RuleMode describes how rules will be interpreted.
Functions§
- Create a mock HTTP client that works with the interceptor using existing utilities