mocktail is a minimal crate for mocking HTTP and gRPC servers in Rust, with native support for streaming.
- Mocks HTTP and gRPC servers
- Mocks defined in Rust using a simple, ergonomic API
- Provides first-class support for streaming
- Supports delayed streaming responses to simulate slow networks or rate-limited APIs
- Supports gRPC unary, client-streaming, server-streaming, and bidirectional-streaming methods
- Match requests to mock responses using built-in matchers or custom matchers
- Fully asynchronous
-
Add
mocktailtoCargo.tomlas a development dependency:[dev-dependencies] mocktail = "0.3.0"
-
Basic usage example:
use mocktail::prelude::*; #[tokio::test] async fn test_example() -> Result<(), Box<dyn std::error::Error>> { // Create a mock set let mut mocks = MockSet::new(); // Build a mock that returns a "hello world!" response // to POST requests to the /hello endpoint with the text "world" // in the body. mocks.mock(|when, then| { when.post().path("/hello").text("world"); then.text("hello world!"); }); // Create and start a mock server let mut server = MockServer::new_http("example").with_mocks(mocks); server.start().await?; // Create a client let client = reqwest::Client::builder().http2_prior_knowledge().build()?; // Send a request that matches the mock created above let response = client .post(server.url("/hello")) .body("world") .send() .await?; assert_eq!(response.status(), http::StatusCode::OK); let body = response.text().await?; assert_eq!(body, "hello world!"); // Send a request that doesn't match a mock let response = client.get(server.url("/nope")).send().await?; assert_eq!(response.status(), http::StatusCode::NOT_FOUND); // Mocks can also be registered to the server directly // Register a mock that will match the request above that returned 404 server.mock(|when, then| { when.get().path("/nope"); then.text("yep!"); }); // Send the request again, it should now match let response = client.get(server.url("/nope")).send().await?; assert_eq!(response.status(), http::StatusCode::OK); let body = response.text().await?; assert_eq!(body, "yep!"); // Mocks can be cleared from the server, enabling server reuse server.mocks.clear(); Ok(()) }
You can simulate slow networks or rate-limited APIs by adding delays between response chunks:
use mocktail::prelude::*;
use std::time::Duration;
#[tokio::test]
async fn test_delayed_streaming() -> Result<(), Box<dyn std::error::Error>> {
let mut mocks = MockSet::new();
// Create a mock with delays between chunks
mocks.mock(|when, then| {
when.get().path("/slow-api");
then.bytes_stream_with_delays([
BodyAction::Bytes("chunk1\n".into()),
BodyAction::Delay(Duration::from_millis(100)),
BodyAction::Bytes("chunk2\n".into()),
BodyAction::Delay(Duration::from_millis(100)),
BodyAction::Bytes("chunk3\n".into()),
]);
});
let server = MockServer::new_http("test").with_mocks(mocks);
server.start().await?;
// The response will stream chunks with the specified delays
let response = reqwest::get(server.url("/slow-api")).await?;
// ... process streamed response
Ok(())
}See examples in the mocktail-tests crate.
This crate takes inspiration from other great mocking libraries including: