🪟 Windows TippsAndroid 17: Neue Version ist hier – Das ist alles neu(16.09.2026 um 11:40 Uhr)
🕵️ Hacking12 Best CASB Solutions Compared (2026): Features & Pricing(16.09.2026 um 09:31 Uhr)
🕵️ Hacking12 Best CIEM Tools Compared (2026): Features & Pricing(16.09.2026 um 09:37 Uhr)
🪟 Windows TippsAndroid 17: Neue Version ist hier – Das ist alles neu(16.09.2026 um 11:40 Uhr)
🕵️ Hacking12 Best CASB Solutions Compared (2026): Features & Pricing(16.09.2026 um 09:31 Uhr)
🕵️ Hacking12 Best CIEM Tools Compared (2026): Features & Pricing(16.09.2026 um 09:37 Uhr)

🔧 Programmierung 🕛 vor 3 Monaten 7 Min Lesezeit
0

Testing in Rust: from cargo test to mocking HTTP calls

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

People love to repeat that in Rust "if it compiles, it works". The compiler does kill a whole class of bugs, but it doesn't check your logic. A wrong discount calculation compiles just fine. So does a mixed-up header in a request to a payment API. You still need tests, and writing them in Rust is easy enough: the runner is built into the toolchain, no jest/pytest to install.



A quick look at the basics first, then the painful part: testing code that talks HTTP.






What the language gives you



A minimal test with zero dependencies:




CODE
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn discount_is_applied() {
assert_eq!(apply_discount(100, 10), 90);
}
}






cargo test and you're done. Unit tests live next to the code and can see private functions. Integration tests go into the tests/ directory and treat the crate as an external consumer, public API only.



And then there are doc tests. Code examples in your docs get compiled and executed on every run. An outdated example in the README simply won't pass CI. Small thing, but it keeps you honest.



As for third-party stuff, the usual suspects in dev-dependencies are - property-based testing, where you describe an invariant and the library generates hundreds of inputs and shrinks the counterexample for you. Some people prefer . Async tests get wrapped in #[tokio::test].



One thing you don't get out of the box at all is parametrized tests. generates a MockPaymentGateway with configurable expectations: which method gets called, with what arguments and how many times. These tests are fast and depend on nothing external.



The catch is that you're mocking your own abstraction, not the actual interaction. A trait mock will confirm you called charge(100). Whether a correct POST with the right Content-Type actually went over the wire, or what happens when the server answers with a 503 - none of that is covered.






Spin up a real server



The second way is to run a local HTTP server right inside the test and have it pretend to be the external API. Client code doesn't change, all you need is a configurable base URL. The request goes through the whole stack for real, down to TCP.



is async-first, inspired by the Java library of the same name. Composable matchers, fits naturally with tokio:




CODE
Mock::given(method("POST"))
.and(path("/orders"))
.respond_with(ResponseTemplate::new(201))
.expect(1)
.mount(&mock_server)
.await;






If your service runs on axum, you'll probably pick this one and won't regret it.



is a recent one. It runs in a background thread with no async runtime, which is handy for sync tests, but the more interesting part is that it splits matchers into routing (when) and validation (should):




CODE
let server = whyhttp::Whyhttp::run();

server
.when().path("/orders").method("POST") // which request we serve
.should().body(r#"{"qty":1}"#) // what it must look like
.response().status(201);






Here's why the split matters. In most libraries the request body is part of the matching condition. Send {"qty":2} instead of {"qty":1} and the mock "isn't found", so the client gets a 404 and the test blows up somewhere in deserialization with a message that has nothing to do with the actual cause. With whyhttp that request still gets its 201 and the scenario runs to the end, while the body mismatch shows up in the final report telling you exactly which check failed. The report is printed when the server is dropped, so forgetting to call verify() and shipping an evergreen test just can't happen here.



Conceptually all four work the same (set up expectations, swap the URL), so migrating between them is cheap.






A third way: run the real dependency in a container



Sometimes you don't want to mock at all. A mock of S3 is always somebody's fantasy about how S3 behaves, and the subtle stuff like listing pagination or multipart uploads will be wrong in it. Same goes for databases, queues and really any sufficiently complex service.



This is where the testcontainers approach has been gaining traction: the test starts a docker container with the real dependency, runs against it and kills the container afterwards. In Rust that's the with ready-made images:




CODE
use testcontainers_modules::{minio::MinIO, testcontainers::runners::AsyncRunner};

#[tokio::test]
async fn uploads_report_to_s3() {
let container = MinIO::default().start().await.unwrap();
let port = container.get_host_port_ipv4(9000).await.unwrap();

let client = s3_client(&format!("http://localhost:{port}"));
// from here on we're talking to a real S3-compatible store
}






Instead of an S3 mock you get a real MinIO. For a third-party API, grab its official docker image, or LocalStack if it's AWS. The price is speed: a container takes seconds to start, not milliseconds, and CI needs docker access. So containers don't replace HTTP mocks, they complement them: mocks for testing your client (those headers and retries), containers for when the behavior of the dependency itself matters.



By the way, don't confuse this with VS Code devcontainers - those solve a different problem, reproducible dev environments. Though they help with testing too: the same image for the whole team and CI means "works on my machine" stops being an argument.






Putting it together



Business logic gets plain unit tests plus property-based ones, no network, no mocks. The more logic you pull out into pure functions, the cheaper it is to cover.



Integration with external APIs goes through a local mock server. That's where you check serialization, headers, reactions to error statuses, retries. For an HTTP client this is the most valuable layer: it catches exactly the bugs that trait mocks miss.



For orchestration ("payment failed -> order marked as failed") trait mocks from mockall are enough, bytes on the wire don't matter there. And where the dependency's behavior is more complex than you'd care to mock, testcontainers it is.



Plus a couple of real e2e tests against actual staging, in a separate suite that doesn't block CI and whose flaky nature everyone has honestly agreed on.






Wrapping up



Testing HTTP code in Rust used to boil down to "hide everything behind traits and suffer". These days a mock server is one line away, and libraries compete on ergonomics rather than feature lists: how good the error reports are, whether you can even forget to verify.



The compiler still won't check that the JSON you send is the right one. But the test for that now takes a minute to write.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
2 Quellen
CVE-2026-88255 | ZenHive mpp up to 0.16.1 Duplicate Submission Gate lib/mpp/replay.ex reserve_hash_atomic input validation (EUVD-2026-80256)
1 Quelle
Android 17: Neue Version ist hier – Das ist alles neu
1 Quelle
Die entscheidende Hürde: Xpeng will deutsch und nicht chinesisch sein
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Testing in Rust: from cargo test to mocking HTTP calls

Thematisch verwandte Begriffe: Testing, Rust, from, cargo · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...