Description: add feature fences for tests
Author: Jonas Smedegaard <dr@jones.dk>
Last-Update: 2025-03-17
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/axum/src/extract/connect_info.rs
+++ b/axum/src/extract/connect_info.rs
@@ -31,6 +31,7 @@
     _connect_info: PhantomData<fn() -> C>,
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[test]
 fn traits() {
     use crate::test_helpers::*;
@@ -221,6 +222,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use super::*;
--- a/axum/src/response/sse.rs
+++ b/axum/src/response/sse.rs
@@ -545,6 +545,7 @@
         assert_eq!(&*leading_space.finalize(), b"data:  foobar\n\n");
     }
 
+    #[cfg(feature = "json")]
     #[test]
     fn valid_json_raw_value_chars_stripped() {
         let json_string = "{\r\"foo\":  \n\r\r   \"bar\\n\"\n}";
@@ -557,6 +558,7 @@
         );
     }
 
+    #[cfg(all(feature = "json", feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn basic() {
         let app = Router::new().route(
@@ -600,6 +602,7 @@
         assert!(stream.chunk_text().await.is_none());
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[tokio::test(start_paused = true)]
     async fn keep_alive() {
         const DELAY: Duration = Duration::from_secs(5);
@@ -636,6 +639,7 @@
         }
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[tokio::test(start_paused = true)]
     async fn keep_alive_ends_when_the_stream_ends() {
         const DELAY: Duration = Duration::from_secs(5);
--- a/axum/src/routing/method_routing.rs
+++ b/axum/src/routing/method_routing.rs
@@ -679,7 +679,7 @@
     /// This allows you to serve a single `MethodRouter` if you don't need any
     /// routing based on the path:
     ///
-    /// ```rust
+    /// ```rust,ignore
     /// use axum::{
     ///     handler::Handler,
     ///     http::{Uri, Method},
@@ -710,7 +710,7 @@
     ///
     /// See [`Router::into_make_service_with_connect_info`] for more details.
     ///
-    /// ```rust
+    /// ```rust,ignore
     /// use axum::{
     ///     handler::Handler,
     ///     response::IntoResponse,
@@ -1435,6 +1435,7 @@
         assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[allow(dead_code)]
     async fn building_complex_router() {
         let app = crate::Router::new().route(
--- a/axum/src/routing/tests/merge.rs
+++ b/axum/src/routing/tests/merge.rs
@@ -1,8 +1,12 @@
 use super::*;
+
+#[cfg(feature = "original-uri")]
 use crate::extract::OriginalUri;
+
 use serde_json::{json, Value};
 use tower::limit::ConcurrencyLimitLayer;
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn basic() {
     let one = Router::new()
@@ -26,6 +30,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn multiple_ors_balanced_differently() {
     let one = Router::new().route("/one", get(|| async { "one" }));
@@ -71,6 +76,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_or() {
     let bar = Router::new().route("/bar", get(|| async { "bar" }));
@@ -87,6 +93,7 @@
     assert_eq!(client.get("/foo/baz").await.text().await, "baz");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn or_with_route_following() {
     let one = Router::new().route("/one", get(|| async { "one" }));
@@ -105,6 +112,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn layer() {
     let one = Router::new().route("/foo", get(|| async {}));
@@ -122,6 +130,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn layer_and_handle_error() {
     let one = Router::new().route("/foo", get(|| async {}));
@@ -138,6 +147,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting() {
     let one = Router::new().route("/foo", get(|| async {}));
@@ -150,6 +160,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn boxed() {
     let one = Router::new().route("/foo", get(|| async {}));
@@ -162,6 +173,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn many_ors() {
     let app = Router::new()
@@ -184,6 +196,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn services() {
     use crate::routing::get_service;
@@ -211,6 +224,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "json", feature = "original-uri"))]
 async fn all_the_uris(
     uri: Uri,
     OriginalUri(original_uri): OriginalUri,
@@ -223,6 +237,7 @@
     }))
 }
 
+#[cfg(all(feature = "original-uri", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_and_seeing_the_right_uri() {
     let one = Router::new().nest("/foo/", Router::new().route("/bar", get(all_the_uris)));
@@ -253,6 +268,7 @@
     );
 }
 
+#[cfg(all(feature = "original-uri", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() {
     let one = Router::new().nest(
@@ -286,6 +302,7 @@
     );
 }
 
+#[cfg(all(feature = "original-uri", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_and_seeing_the_right_uri_ors_with_nesting() {
     let one = Router::new().nest(
@@ -331,6 +348,7 @@
     );
 }
 
+#[cfg(all(feature = "original-uri", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_and_seeing_the_right_uri_ors_with_multi_segment_uris() {
     let one = Router::new().nest(
@@ -364,6 +382,7 @@
     );
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn middleware_that_return_early() {
     let private = Router::new()
--- a/axum/src/routing/tests/mod.rs
+++ b/axum/src/routing/tests/mod.rs
@@ -14,8 +14,12 @@
         *,
     },
     util::mutex_num_locked,
-    BoxError, Extension, Json, Router, ServiceExt,
+    BoxError, Extension, Router, ServiceExt,
 };
+
+#[cfg(feature = "json")]
+use crate::Json;
+
 use axum_core::extract::Request;
 use counting_cloneable_state::CountingCloneableState;
 use futures_util::stream::StreamExt;
@@ -46,6 +50,7 @@
 mod merge;
 mod nest;
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn hello_world() {
     async fn root(_: Request) -> &'static str {
@@ -79,6 +84,7 @@
     assert_eq!(body, "users#create");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn routing() {
     let app = Router::new()
@@ -114,6 +120,7 @@
     assert_eq!(res.text().await, "users#action");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn router_type_doesnt_change() {
     let app: Router = Router::new()
@@ -135,6 +142,7 @@
     assert_eq!(res.text().await, "hi from POST");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn routing_between_services() {
     use std::convert::Infallible;
@@ -181,6 +189,7 @@
     assert_eq!(res.text().await, "handler");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn middleware_on_single_route() {
     use tower_http::trace::TraceLayer;
@@ -199,6 +208,7 @@
     assert_eq!(body, "Hello, World!");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn service_in_bottom() {
     async fn handler(_req: Request) -> Result<Response<Body>, Infallible> {
@@ -210,6 +220,7 @@
     TestClient::new(app);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn wrong_method_handler() {
     let app = Router::new()
@@ -233,6 +244,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn wrong_method_service() {
     #[derive(Clone)]
@@ -273,6 +285,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn multiple_methods_for_one_handler() {
     async fn root(_: Request) -> &'static str {
@@ -290,6 +303,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn wildcard_sees_whole_url() {
     let app = Router::new().route("/api/*rest", get(|uri: Uri| async move { uri.to_string() }));
@@ -300,6 +314,7 @@
     assert_eq!(res.text().await, "/api/foo/bar");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn middleware_applies_to_routes_above() {
     let app = Router::new()
@@ -316,6 +331,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn not_found_for_extra_trailing_slash() {
     let app = Router::new().route("/foo", get(|| async {}));
@@ -329,6 +345,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn not_found_for_missing_trailing_slash() {
     let app = Router::new().route("/foo/", get(|| async {}));
@@ -339,6 +356,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn with_and_without_trailing_slash() {
     let app = Router::new()
@@ -357,6 +375,7 @@
 }
 
 // for https://github.com/tokio-rs/axum/issues/420
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn wildcard_doesnt_match_just_trailing_slash() {
     let app = Router::new().route(
@@ -377,6 +396,7 @@
     assert_eq!(res.text().await, "foo/bar");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn what_matches_wildcard() {
     let app = Router::new()
@@ -405,6 +425,7 @@
     assert_eq!(get("/x/a/b/").await, "x");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn static_and_dynamic_paths() {
     let app = Router::new()
@@ -423,6 +444,7 @@
     assert_eq!(res.text().await, "static");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 #[should_panic(expected = "Paths must start with a `/`. Use \"/\" for root routes")]
 async fn empty_route() {
@@ -430,6 +452,7 @@
     TestClient::new(app);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn middleware_still_run_for_unmatched_requests() {
     #[derive(Clone)]
@@ -470,6 +493,7 @@
     assert_eq!(COUNT.load(Ordering::SeqCst), 2);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 #[should_panic(expected = "\
     Invalid route: `Router::route_service` cannot be used with `Router`s. \
@@ -479,6 +503,7 @@
     TestClient::new(Router::new().route_service("/", Router::new()));
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn route_layer() {
     let app = Router::new()
@@ -506,6 +531,7 @@
     assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn different_methods_added_in_different_routes() {
     let app = Router::new()
@@ -523,6 +549,7 @@
     assert_eq!(body, "POST");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 #[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")]
 async fn merging_routers_with_fallbacks_panics() {
@@ -549,6 +576,7 @@
     _ = app.clone().merge(app);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn merging_routers_with_same_paths_but_different_methods() {
     let one = Router::new().route("/", get(|| async { "GET" }));
@@ -565,6 +593,7 @@
     assert_eq!(body, "POST");
 }
 
+#[cfg(all(feature = "json", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn head_content_length_through_hyper_server() {
     let app = Router::new()
@@ -582,6 +611,7 @@
     assert!(res.text().await.is_empty());
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn head_content_length_through_hyper_server_that_hits_fallback() {
     let app = Router::new().fallback(|| async { "foo" });
@@ -592,6 +622,7 @@
     assert_eq!(res.headers()["content-length"], "3");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn head_with_middleware_applied() {
     use tower_http::compression::{predicate::SizeAbove, CompressionLayer};
@@ -620,6 +651,7 @@
     assert!(!res.headers().contains_key("content-length"));
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 #[should_panic(expected = "Paths must start with a `/`")]
 async fn routes_must_start_with_slash() {
@@ -627,6 +659,8 @@
     TestClient::new(app);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
+#[cfg(all(feature = "json", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn body_limited_by_default() {
     let app = Router::new()
@@ -655,6 +689,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn disabling_the_default_limit() {
     let app = Router::new()
@@ -671,6 +706,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn limited_body_with_content_length() {
     const LIMIT: usize = 3;
@@ -693,6 +729,7 @@
     assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn changing_the_default_limit() {
     let new_limit = 2;
@@ -716,6 +753,7 @@
     assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn changing_the_default_limit_differently_on_different_routes() {
     let limit1 = 2;
@@ -778,6 +816,7 @@
     assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn limited_body_with_streaming_body() {
     const LIMIT: usize = 3;
@@ -808,6 +847,7 @@
     assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn extract_state() {
     #[derive(Clone)]
@@ -844,6 +884,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn explicitly_set_state() {
     let app = Router::new()
@@ -858,6 +899,7 @@
     assert_eq!(res.text().await, "foo");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn layer_response_into_response() {
     fn map_response<B>(_res: Response<B>) -> Result<Response<B>, impl IntoResponse> {
@@ -905,6 +947,7 @@
     assert_eq!(path_for_nested_route("/a/", "/b/"), "/a/b/");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn state_isnt_cloned_too_much() {
     let state = CountingCloneableState::new();
@@ -923,6 +966,7 @@
     assert_eq!(state.count(), 3);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn state_isnt_cloned_too_much_in_layer() {
     async fn layer(State(_): State<CountingCloneableState>, req: Request, next: Next) -> Response {
@@ -943,6 +987,7 @@
     assert_eq!(state.count(), 3);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn logging_rejections() {
     #[derive(Deserialize, Eq, PartialEq, Debug)]
@@ -1045,6 +1090,7 @@
     assert!(body.is_empty());
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn impl_handler_for_into_response() {
     let app = Router::new().route("/things", post((StatusCode::CREATED, "thing created")));
@@ -1088,6 +1134,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn middleware_adding_body() {
     let app = Router::new()
--- a/axum/src/routing/tests/nest.rs
+++ b/axum/src/routing/tests/nest.rs
@@ -2,6 +2,7 @@
 use std::collections::HashMap;
 use tower_http::services::ServeDir;
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_apps() {
     let api_routes = Router::new()
@@ -57,6 +58,7 @@
     assert_eq!(res.text().await, "v0: games#show (123)");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn wrong_method_nest() {
     let nested_app = Router::new().route("/", get(|| async {}));
@@ -75,6 +77,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_router_at_root() {
     let nested = Router::new().route("/foo", get(|uri: Uri| async move { uri.to_string() }));
@@ -93,6 +96,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_router_at_empty_path() {
     let nested = Router::new().route("/foo", get(|uri: Uri| async move { uri.to_string() }));
@@ -111,6 +115,7 @@
     assert_eq!(res.status(), StatusCode::NOT_FOUND);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nesting_handler_at_root() {
     let app = Router::new().nest_service("/", get(|uri: Uri| async move { uri.to_string() }));
@@ -130,6 +135,7 @@
     assert_eq!(res.text().await, "/foo/bar");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_url_extractor() {
     let app = Router::new().nest(
@@ -156,6 +162,7 @@
     assert_eq!(res.text().await, "/qux");
 }
 
+#[cfg(all(feature = "original-uri", feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_url_original_extractor() {
     let app = Router::new().nest(
@@ -176,6 +183,7 @@
     assert_eq!(res.text().await, "/foo/bar/baz");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_service_sees_stripped_uri() {
     let app = Router::new().nest(
@@ -199,6 +207,7 @@
     assert_eq!(res.text().await, "/baz");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nest_static_file_server() {
     let app = Router::new().nest_service("/static", ServeDir::new("."));
@@ -209,6 +218,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_multiple_routes() {
     let app = Router::new()
@@ -242,6 +252,7 @@
         .route("/", get(|| async {}));
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn multiple_top_level_nests() {
     let app = Router::new()
@@ -266,6 +277,7 @@
     _ = Router::<()>::new().nest("/one/*rest", Router::new());
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn outer_middleware_still_see_whole_url() {
     #[derive(Clone)]
@@ -314,6 +326,7 @@
     assert_eq!(client.get("/one/two").await.text().await, "/one/two");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nest_at_capture() {
     let api_routes = Router::new().route(
@@ -330,6 +343,7 @@
     assert_eq!(res.text().await, "a=foo b=bar");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nest_with_and_without_trailing() {
     let app = Router::new().nest_service("/foo", get(|| async {}));
@@ -346,6 +360,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[tokio::test]
 async fn nesting_with_root_inner_router() {
     let app = Router::new()
@@ -382,6 +397,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 macro_rules! nested_route_test {
     (
         $name:ident,
@@ -405,15 +421,27 @@
 }
 
 // test cases taken from https://github.com/tokio-rs/axum/issues/714#issuecomment-1058144460
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_1, nest = "", route = "/", expected = "/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_2, nest = "", route = "/a", expected = "/a");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_3, nest = "", route = "/a/", expected = "/a/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_4, nest = "/", route = "/", expected = "/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_5, nest = "/", route = "/a", expected = "/a");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_6, nest = "/", route = "/a/", expected = "/a/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_7, nest = "/a", route = "/", expected = "/a");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_8, nest = "/a", route = "/a", expected = "/a/a");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_9, nest = "/a", route = "/a/", expected = "/a/a/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_11, nest = "/a/", route = "/", expected = "/a/");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_12, nest = "/a/", route = "/a", expected = "/a/a");
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 nested_route_test!(nest_13, nest = "/a/", route = "/a/", expected = "/a/a/");
--- a/axum/src/test_helpers/mod.rs
+++ b/axum/src/test_helpers/mod.rs
@@ -1,8 +1,13 @@
 #![allow(clippy::disallowed_names)]
 
-use crate::{extract::Request, response::Response, serve};
+use crate::{extract::Request, response::Response};
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
+use crate::serve;
+
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 mod test_client;
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 pub(crate) use self::test_client::*;
 
 pub(crate) mod tracing_helpers;
--- a/axum/src/extract/host.rs
+++ b/axum/src/extract/host.rs
@@ -76,6 +76,7 @@
     })
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use super::*;
--- a/axum/src/extract/query.rs
+++ b/axum/src/extract/query.rs
@@ -97,6 +97,7 @@
 
 axum_core::__impl_deref!(Query);
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use crate::{routing::get, test_helpers::TestClient, Router};
--- a/axum/src/extract/ws.rs
+++ b/axum/src/extract/ws.rs
@@ -835,6 +835,7 @@
     pub const AGAIN: u16 = 1013;
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use std::future::ready;
--- a/axum/src/form.rs
+++ b/axum/src/form.rs
@@ -122,6 +122,7 @@
 
 axum_core::__impl_deref!(Form);
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use crate::{
--- a/axum/src/middleware/map_request.rs
+++ b/axum/src/middleware/map_request.rs
@@ -385,6 +385,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     use super::*;
--- a/axum/src/middleware/map_response.rs
+++ b/axum/src/middleware/map_response.rs
@@ -341,6 +341,7 @@
     }
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[cfg(test)]
 mod tests {
     #[allow(unused_imports)]
--- a/axum/src/extract/matched_path.rs
+++ b/axum/src/extract/matched_path.rs
@@ -140,6 +140,7 @@
     };
     use http::StatusCode;
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_on_handler() {
         let app = Router::new().route(
@@ -153,6 +154,7 @@
         assert_eq!(res.text().await, "/:a");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_on_handler_in_nested_router() {
         let app = Router::new().nest(
@@ -169,6 +171,7 @@
         assert_eq!(res.text().await, "/:a/:b");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_on_handler_in_deeply_nested_router() {
         let app = Router::new().nest(
@@ -188,6 +191,7 @@
         assert_eq!(res.text().await, "/:a/:b/:c");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn cannot_extract_nested_matched_path_in_middleware() {
         async fn extract_matched_path<B>(
@@ -208,6 +212,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn can_extract_nested_matched_path_in_middleware_using_nest() {
         async fn extract_matched_path<B>(
@@ -228,6 +233,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn cannot_extract_nested_matched_path_in_middleware_via_extension() {
         async fn assert_no_matched_path<B>(req: Request<B>) -> Request<B> {
@@ -245,6 +251,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[tokio::test]
     async fn can_extract_nested_matched_path_in_middleware_via_extension_using_nest() {
         async fn assert_matched_path<B>(req: Request<B>) -> Request<B> {
@@ -262,6 +269,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn can_extract_nested_matched_path_in_middleware_on_nested_router() {
         async fn extract_matched_path<B>(matched_path: MatchedPath, req: Request<B>) -> Request<B> {
@@ -282,6 +290,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn can_extract_nested_matched_path_in_middleware_on_nested_router_via_extension() {
         async fn extract_matched_path<B>(req: Request<B>) -> Request<B> {
@@ -303,6 +312,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_on_nested_handler() {
         async fn handler(path: Option<MatchedPath>) {
@@ -318,6 +328,7 @@
     }
 
     // https://github.com/tokio-rs/axum/issues/1579
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn doesnt_panic_if_router_called_from_wildcard_route() {
         use tower::ServiceExt;
@@ -337,6 +348,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn cant_extract_in_fallback() {
         async fn handler(path: Option<MatchedPath>, req: Request) {
--- a/axum/src/extract/mod.rs
+++ b/axum/src/extract/mod.rs
@@ -99,6 +99,7 @@
 mod tests {
     use crate::{routing::get, test_helpers::*, Router};
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn consume_body() {
         let app = Router::new().route("/", get(|body: String| async { body }));
--- a/axum/src/extract/nested_path.rs
+++ b/axum/src/extract/nested_path.rs
@@ -121,6 +121,7 @@
         Router,
     };
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn one_level_of_nesting() {
         let api = Router::new().route(
@@ -139,6 +140,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn one_level_of_nesting_with_trailing_slash() {
         let api = Router::new().route(
@@ -157,6 +159,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn two_levels_of_nesting() {
         let api = Router::new().route(
@@ -175,6 +178,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn two_levels_of_nesting_with_trailing_slash() {
         let api = Router::new().route(
@@ -193,6 +197,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn nested_at_root() {
         let api = Router::new().route(
@@ -211,6 +216,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn deeply_nested_from_root() {
         let api = Router::new().route(
@@ -229,6 +235,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn in_fallbacks() {
         let api = Router::new().fallback(get(|nested_path: NestedPath| {
@@ -244,6 +251,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn in_middleware() {
         async fn middleware(nested_path: NestedPath, req: Request, next: Next) -> Response {
--- a/axum/src/extract/path/mod.rs
+++ b/axum/src/extract/path/mod.rs
@@ -548,6 +548,7 @@
     use serde::Deserialize;
     use std::collections::HashMap;
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_url_params() {
         let app = Router::new().route(
@@ -569,6 +570,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extracting_url_params_multiple_times() {
         let app = Router::new().route("/users/:id", get(|_: Path<i32>, _: Path<String>| async {}));
@@ -579,6 +581,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn percent_decoding() {
         let app = Router::new().route(
@@ -593,6 +596,7 @@
         assert_eq!(res.text().await, "one two");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn supports_128_bit_numbers() {
         let app = Router::new()
@@ -614,6 +618,7 @@
         assert_eq!(res.text().await, "123");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn wildcard() {
         let app = Router::new()
@@ -637,6 +642,7 @@
         assert_eq!(res.text().await, "baz/qux");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn captures_dont_match_empty_path() {
         let app = Router::new().route("/:key", get(|| async {}));
@@ -650,6 +656,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn captures_match_empty_inner_segments() {
         let app = Router::new().route(
@@ -666,6 +673,7 @@
         assert_eq!(res.text().await, "");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn captures_match_empty_inner_segments_near_end() {
         let app = Router::new().route(
@@ -685,6 +693,7 @@
         assert_eq!(res.text().await, "");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn captures_match_empty_trailing_segment() {
         let app = Router::new().route(
@@ -707,6 +716,7 @@
         assert_eq!(res.status(), StatusCode::NOT_FOUND);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn str_reference_deserialize() {
         struct Param(String);
@@ -732,6 +742,7 @@
         assert_eq!(res.text().await, "foo bar");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn two_path_extractors() {
         let app = Router::new().route("/:a/:b", get(|_: Path<String>, _: Path<String>| async {}));
@@ -747,6 +758,7 @@
         );
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn deserialize_into_vec_of_tuples() {
         let app = Router::new().route(
@@ -768,6 +780,7 @@
         assert_eq!(res.status(), StatusCode::OK);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn type_that_uses_deserialize_any() {
         use time::Date;
@@ -846,6 +859,7 @@
         assert_eq!(res.text().await, "struct: 2023-01-01 2023-01-02 2023-01-03");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn wrong_number_of_parameters_json() {
         use serde_json::Value;
@@ -869,6 +883,7 @@
             .starts_with("Wrong number of path arguments for `Path`. Expected 1 but got 2"));
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn raw_path_params() {
         let app = Router::new().route(
--- a/axum/src/extract/request_parts.rs
+++ b/axum/src/extract/request_parts.rs
@@ -94,6 +94,7 @@
     use crate::{extract::Extension, routing::get, test_helpers::*, Router};
     use http::{Method, StatusCode};
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn extract_request_parts() {
         #[derive(Clone)]
--- a/axum/src/handler/mod.rs
+++ b/axum/src/handler/mod.rs
@@ -106,7 +106,7 @@
 /// The `Handler` trait is also implemented for `T: IntoResponse`. That allows easily returning
 /// fixed data for routes:
 ///
-/// ```
+/// ```ignore
 /// use axum::{
 ///     Router,
 ///     routing::{get, post},
@@ -158,7 +158,7 @@
     /// Adding the [`tower::limit::ConcurrencyLimit`] middleware to a handler
     /// can be done like so:
     ///
-    /// ```rust
+    /// ```rust,ignore
     /// use axum::{
     ///     routing::get,
     ///     handler::Handler,
@@ -395,6 +395,7 @@
         map_response_body::MapResponseBodyLayer, timeout::TimeoutLayer,
     };
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn handler_into_service() {
         async fn handle(body: String) -> impl IntoResponse {
@@ -408,6 +409,7 @@
         assert_eq!(res.text().await, "you said: hi there!");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn with_layer_that_changes_request_body_and_state() {
         async fn handle(State(state): State<&'static str>) -> &'static str {
--- a/axum/src/json.rs
+++ b/axum/src/json.rs
@@ -220,6 +220,7 @@
     use serde::Deserialize;
     use serde_json::{json, Value};
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn deserialize_body() {
         #[derive(Debug, Deserialize)]
@@ -236,6 +237,7 @@
         assert_eq!(body, "bar");
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn consume_body_to_json_requires_json_content_type() {
         #[derive(Debug, Deserialize)]
@@ -253,6 +255,7 @@
         assert_eq!(status, StatusCode::UNSUPPORTED_MEDIA_TYPE);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn json_content_types() {
         async fn valid_json_content_type(content_type: &str) -> bool {
@@ -276,6 +279,7 @@
         assert!(!valid_json_content_type("text/json").await);
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn invalid_json_syntax() {
         let app = Router::new().route("/", post(|_: Json<serde_json::Value>| async {}));
@@ -306,6 +310,7 @@
         y: i32,
     }
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn invalid_json_data() {
         let app = Router::new().route("/", post(|_: Json<Foo>| async {}));
--- a/axum/src/middleware/from_extractor.rs
+++ b/axum/src/middleware/from_extractor.rs
@@ -308,6 +308,7 @@
     use http::{header, request::Parts, StatusCode};
     use tower_http::limit::RequestBodyLimitLayer;
 
+    #[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
     #[crate::test]
     async fn test_from_extractor() {
         #[derive(Clone)]
--- a/axum/src/routing/tests/fallback.rs
+++ b/axum/src/routing/tests/fallback.rs
@@ -1,6 +1,7 @@
 use super::*;
 use crate::middleware::{map_request, map_response};
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn basic() {
     let app = Router::new()
@@ -16,6 +17,7 @@
     assert_eq!(res.text().await, "fallback");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nest() {
     let app = Router::new()
@@ -31,6 +33,7 @@
     assert_eq!(res.text().await, "fallback");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn two() {
     let app = Router::new()
@@ -43,6 +46,7 @@
     assert_eq!(res.text().await, "fallback");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn or() {
     let one = Router::new().route("/one", get(|| async {}));
@@ -60,6 +64,7 @@
     assert_eq!(res.text().await, "fallback");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn fallback_accessing_state() {
     let app = Router::new()
@@ -81,6 +86,7 @@
     (StatusCode::NOT_FOUND, "outer")
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nested_router_inherits_fallback() {
     let inner = Router::new();
@@ -93,6 +99,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn doesnt_inherit_fallback_if_overridden() {
     let inner = Router::new().fallback(inner_fallback);
@@ -109,6 +116,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn deeply_nested_inherit_from_top() {
     let app = Router::new()
@@ -122,6 +130,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn deeply_nested_inherit_from_middle() {
     let app = Router::new().nest(
@@ -138,6 +147,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn with_middleware_on_inner_fallback() {
     async fn never_called<B>(_: Request<B>) -> Request<B> {
@@ -154,6 +164,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn also_inherits_default_layered_fallback() {
     async fn set_header<B>(mut res: Response<B>) -> Response<B> {
@@ -176,6 +187,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn nest_fallback_on_inner() {
     let app = Router::new()
@@ -195,6 +207,7 @@
 }
 
 // https://github.com/tokio-rs/axum/issues/1931
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn doesnt_panic_if_used_with_nested_router() {
     async fn handler() {}
@@ -210,6 +223,7 @@
     assert_eq!(res.status(), StatusCode::OK);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn issue_2072() {
     let nested_routes = Router::new().fallback(inner_fallback);
@@ -229,6 +243,7 @@
     assert_eq!(res.text().await, "");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn issue_2072_outer_fallback_before_merge() {
     let nested_routes = Router::new().fallback(inner_fallback);
@@ -249,6 +264,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn issue_2072_outer_fallback_after_merge() {
     let nested_routes = Router::new().fallback(inner_fallback);
@@ -269,6 +285,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn merge_router_with_fallback_into_nested_router_with_fallback() {
     let nested_routes = Router::new().fallback(inner_fallback);
@@ -288,6 +305,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn merging_nested_router_with_fallback_into_router_with_fallback() {
     let nested_routes = Router::new().fallback(inner_fallback);
@@ -307,6 +325,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn merge_empty_into_router_with_fallback() {
     let app = Router::new().fallback(outer_fallback).merge(Router::new());
@@ -318,6 +337,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn merge_router_with_fallback_into_empty() {
     let app = Router::new().merge(Router::new().fallback(outer_fallback));
@@ -329,6 +349,7 @@
     assert_eq!(res.text().await, "outer");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn mna_fallback_with_existing_fallback() {
     let app = Router::new()
@@ -350,6 +371,7 @@
     );
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn mna_fallback_with_state() {
     let app = Router::new()
@@ -362,6 +384,7 @@
     assert_eq!(res.text().await, "state");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn mna_fallback_with_unused_state() {
     let app = Router::new()
@@ -374,6 +397,7 @@
     assert_eq!(res.text().await, "bla");
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn state_isnt_cloned_too_much_with_fallback() {
     let state = CountingCloneableState::new();
--- a/axum/src/routing/tests/handle_error.rs
+++ b/axum/src/routing/tests/handle_error.rs
@@ -12,6 +12,7 @@
     TimeoutLayer::new(Duration::from_millis(10))
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn handler() {
     let app = Router::new().route(
@@ -28,6 +29,7 @@
     assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn handler_multiple_methods_first() {
     let app = Router::new().route(
@@ -45,6 +47,7 @@
     assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn handler_multiple_methods_middle() {
     let app = Router::new().route(
@@ -63,6 +66,7 @@
     assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn handler_multiple_methods_last() {
     let app = Router::new().route(
@@ -79,6 +83,7 @@
     assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
 }
 
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 #[crate::test]
 async fn handler_service_ext() {
     let fallible_service = tower::service_fn(|_| async { Err::<(), ()>(()) });
--- a/axum-extra/src/extract/cookie/mod.rs
+++ b/axum-extra/src/extract/cookie/mod.rs
@@ -37,7 +37,7 @@
 ///
 /// # Example
 ///
-/// ```rust
+/// ```rust,ignore
 /// use axum::{
 ///     Router,
 ///     routing::{post, get},
@@ -230,6 +230,7 @@
     // jar so it cannot be called multiple times.
 }
 
+#[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
 #[cfg(test)]
 mod tests {
     use super::*;
--- a/axum/src/lib.rs
+++ b/axum/src/lib.rs
@@ -24,7 +24,7 @@
 //!
 //! The "Hello, World!" of axum is:
 //!
-//! ```rust,no_run
+//! ```rust,ignore
 //! use axum::{
 //!     routing::get,
 //!     Router,
@@ -78,7 +78,7 @@
 //! An extractor is a type that implements [`FromRequest`] or [`FromRequestParts`]. Extractors are
 //! how you pick apart the incoming request to get the parts your handler needs.
 //!
-//! ```rust
+//! ```rust,ignore
 //! use axum::extract::{Path, Query, Json};
 //! use std::collections::HashMap;
 //!
@@ -100,7 +100,7 @@
 //!
 //! Anything that implements [`IntoResponse`] can be returned from handlers.
 //!
-//! ```rust,no_run
+//! ```rust,ignore
 //! use axum::{
 //!     body::Body,
 //!     routing::get,
@@ -226,7 +226,7 @@
 //!
 //! State can also be passed directly to handlers using closure captures:
 //!
-//! ```rust,no_run
+//! ```rust,ignore
 //! use axum::{
 //!     Json,
 //!     extract::{Extension, Path},
--- a/axum-extra/src/lib.rs
+++ b/axum-extra/src/lib.rs
@@ -110,6 +110,7 @@
     pub const PATH_SEGMENT: &AsciiSet = &PATH.add(b'/').add(b'%');
 }
 
+#[cfg(feature = "typed-routing")]
 #[cfg(test)]
 use axum_macros::__private_axum_test as test;
 
--- a/axum/src/docs/extract.md
+++ b/axum/src/docs/extract.md
@@ -9,7 +9,7 @@
 For example, [`Json`] is an extractor that consumes the request body and
 deserializes it as JSON into some target type:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     extract::Json,
     routing::post,
@@ -36,7 +36,7 @@
 
 Some commonly used extractors are:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     extract::{Request, Json, Path, Extension, Query},
     routing::post,
@@ -91,7 +91,7 @@
 
 You can also apply multiple extractors:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     extract::{Path, Query},
     routing::get,
@@ -205,7 +205,7 @@
 All extractors defined in axum will reject the request if it doesn't match.
 If you wish to make an extractor optional you can wrap it in `Option`:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     extract::Json,
     routing::post,
@@ -228,7 +228,7 @@
 Wrapping extractors in `Result` makes them optional and gives you the reason
 the extraction failed:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     extract::{Json, rejection::JsonRejection},
     routing::post,
@@ -294,7 +294,7 @@
 [`JsonRejection::JsonDataError`]. However it is still possible to access via
 methods from [`std::error::Error`]:
 
-```rust
+```rust,ignore
 use std::error::Error;
 use axum::{
     extract::{Json, rejection::JsonRejection},
--- a/axum/src/docs/middleware.md
+++ b/axum/src/docs/middleware.md
@@ -531,7 +531,7 @@
 The workaround is to wrap the middleware around the entire `Router` (this works
 because `Router` implements [`Service`]):
 
-```rust
+```rust,ignore
 use tower::Layer;
 use axum::{
     Router,
--- a/axum/src/docs/response.md
+++ b/axum/src/docs/response.md
@@ -5,7 +5,7 @@
 Anything that implements [`IntoResponse`] can be returned from a handler. axum
 provides implementations for common types:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     Json,
     response::{Html, IntoResponse},
@@ -68,7 +68,7 @@
 Additionally you can return tuples to build more complex responses from
 individual parts.
 
-```rust,no_run
+```rust,ignore
 use axum::{
     Json,
     response::IntoResponse,
@@ -162,7 +162,7 @@
 
 Use [`Response`] for more low level control:
 
-```rust,no_run
+```rust,ignore
 use axum::{
     Json,
     response::{IntoResponse, Response},
--- a/axum/src/docs/routing/fallback.md
+++ b/axum/src/docs/routing/fallback.md
@@ -34,7 +34,7 @@
 Using `Router::new().fallback(...)` to accept all request regardless of path or
 method, if you don't have other routes, isn't optimal:
 
-```rust
+```rust,ignore
 use axum::Router;
 
 async fn handler() {}
@@ -49,8 +49,8 @@
 
 Running the handler directly is faster since it avoids the overhead of routing:
 
-```rust
-use axum::handler::HandlerWithoutStateExt;
+```rust,ignore
+use axum::handjler::HandlerWithoutStateExt;
 
 async fn handler() {}
 
--- a/axum/src/docs/routing/into_make_service_with_connect_info.md
+++ b/axum/src/docs/routing/into_make_service_with_connect_info.md
@@ -6,7 +6,7 @@
 
 Extracting [`std::net::SocketAddr`] is supported out of the box:
 
-```rust
+```rust,ignore
 use axum::{
     extract::ConnectInfo,
     routing::get,
@@ -28,7 +28,7 @@
 
 You can implement custom a [`Connected`] like so:
 
-```rust
+```rust,ignore
 use axum::{
     extract::connect_info::{ConnectInfo, Connected},
     routing::get,
--- a/axum/src/docs/routing/method_not_allowed_fallback.md
+++ b/axum/src/docs/routing/method_not_allowed_fallback.md
@@ -3,7 +3,7 @@
 Sets a fallback on all previously registered [`MethodRouter`]s,
 to be called when no matching method handler is set.
 
-```rust,no_run
+```rust,ignore
 use axum::{response::IntoResponse, routing::get, Router};
 
 async fn hello_world() -> impl IntoResponse {
--- a/axum/src/docs/routing/nest.md
+++ b/axum/src/docs/routing/nest.md
@@ -114,7 +114,7 @@
 If the nested router has its own fallback then the outer fallback will not be
 inherited:
 
-```rust
+```rust,ignore
 use axum::{
     routing::get,
     http::StatusCode,
--- a/axum/src/docs/routing/with_state.md
+++ b/axum/src/docs/routing/with_state.md
@@ -1,7 +1,7 @@
 Provide the state for the router. State passed to this method is global and will be used
 for all requests this router receives. That means it is not suitable for holding state derived from a request, such as authorization data extracted in a middleware. Use [`Extension`] instead for such data.
 
-```rust
+```rust,ignore
 use axum::{Router, routing::get, extract::State};
 
 #[derive(Clone)]
@@ -24,7 +24,7 @@
 When returning `Router`s from functions, it is generally recommended not to set the
 state directly:
 
-```rust
+```rust,ignore
 use axum::{Router, routing::get, extract::State};
 
 #[derive(Clone)]
@@ -48,7 +48,7 @@
 If you do need to provide the state, and you're _not_ nesting/merging the router
 into another router, then return `Router` without any type parameters:
 
-```rust
+```rust,ignore
 # use axum::{Router, routing::get, extract::State};
 # #[derive(Clone)]
 # struct AppState {}
@@ -76,7 +76,7 @@
 If you are nesting/merging the router it is recommended to use a generic state
 type on the resulting router:
 
-```rust
+```rust,ignore
 # use axum::{Router, routing::get, extract::State};
 # #[derive(Clone)]
 # struct AppState {}
@@ -102,7 +102,7 @@
 
 For example:
 
-```rust
+```rust,ignore
 # use axum::{Router, routing::get, extract::State};
 # #[derive(Clone)]
 # struct AppState {}
@@ -131,7 +131,7 @@
 Perhaps a little counter intuitively, `Router::with_state` doesn't always return a
 `Router<()>`. Instead you get to pick what the new missing state type is:
 
-```rust
+```rust,ignore
 # use axum::{Router, routing::get, extract::State};
 # #[derive(Clone)]
 # struct AppState {}
@@ -185,7 +185,7 @@
 
 Instead return `Router<()>` since we have provided all the state needed:
 
-```rust
+```rust,ignore
 # use axum::{Router, routing::get, extract::State};
 # #[derive(Clone)]
 # struct AppState {}
--- a/axum/src/extract/state.rs
+++ b/axum/src/extract/state.rs
@@ -144,7 +144,7 @@
 ///
 /// # With `Handler`
 ///
-/// ```
+/// ```ignore
 /// use axum::{routing::get, handler::Handler, extract::State};
 ///
 /// #[derive(Clone)]
--- a/axum/src/handler/service.rs
+++ b/axum/src/handler/service.rs
@@ -35,7 +35,7 @@
     ///
     /// This allows you to serve a single handler if you don't need any routing:
     ///
-    /// ```rust
+    /// ```rust,ignore
     /// use axum::{
     ///     handler::Handler,
     ///     extract::State,
@@ -69,7 +69,7 @@
     ///
     /// See [`Router::into_make_service_with_connect_info`] for more details.
     ///
-    /// ```rust
+    /// ```rust,ignore
     /// use axum::{
     ///     handler::Handler,
     ///     response::IntoResponse,
--- a/axum/src/response/mod.rs
+++ b/axum/src/response/mod.rs
@@ -67,7 +67,7 @@
 /// If you specifically want a 204 [`StatusCode::NO_CONTENT`] status, you can use either `StatusCode` type
 /// directly, or this shortcut struct for self-documentation.
 ///
-/// ```
+/// ```ignore
 /// use axum::{extract::Path, response::NoContent};
 ///
 /// async fn delete_user(Path(user): Path<String>) -> Result<NoContent, String> {
--- a/axum/src/routing/mod.rs
+++ b/axum/src/routing/mod.rs
@@ -470,7 +470,7 @@
     /// Convert this router into a [`MakeService`], that is a [`Service`] whose
     /// response is another service.
     ///
-    /// ```
+    /// ```ignore
     /// use axum::{
     ///     routing::get,
     ///     Router,
--- a/axum-extra/src/extract/cookie/private.rs
+++ b/axum-extra/src/extract/cookie/private.rs
@@ -19,7 +19,7 @@
 ///
 /// # Example
 ///
-/// ```rust
+/// ```rust,ignore
 /// use axum::{
 ///     Router,
 ///     routing::{post, get},
--- a/axum-extra/src/extract/cookie/signed.rs
+++ b/axum-extra/src/extract/cookie/signed.rs
@@ -20,7 +20,7 @@
 ///
 /// # Example
 ///
-/// ```rust
+/// ```rust,ignore
 /// use axum::{
 ///     Router,
 ///     routing::{post, get},
@@ -94,7 +94,7 @@
 /// If you have been using `Arc<AppState>` you cannot implement `FromRef<Arc<AppState>> for Key`.
 /// You can use a new type instead:
 ///
-/// ```rust
+/// ```rust,ignore
 /// # use axum::extract::FromRef;
 /// # use axum_extra::extract::cookie::{PrivateCookieJar, Cookie, Key};
 /// use std::sync::Arc;
--- a/axum-extra/src/extract/optional_path.rs
+++ b/axum-extra/src/extract/optional_path.rs
@@ -68,6 +68,7 @@
     use super::OptionalPath;
     use crate::test_helpers::TestClient;
 
+	#[cfg(feature = "typed-routing")]
     #[crate::test]
     async fn supports_128_bit_numbers() {
         async fn handle(OptionalPath(param): OptionalPath<NonZeroU32>) -> String {
--- a/axum/src/extract/multipart.rs
+++ b/axum/src/extract/multipart.rs
@@ -307,6 +307,7 @@
 }
 
 #[cfg(test)]
+#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))]
 mod tests {
     use axum_core::extract::DefaultBodyLimit;
 
