Skip to main content

axum_test/
test_server_builder.rs

1use crate::TestServer;
2use crate::TestServerConfig;
3use crate::Transport;
4use crate::internals::ErrorMessage;
5use crate::transport_layer::IntoTransportLayer;
6use anyhow::Result;
7use std::net::IpAddr;
8
9/// A builder for [`TestServer`]. Inside is a [`TestServerConfig`],
10/// configured by each method, and then turn into a server by [`TestServerBuilder::build`].
11///
12/// The recommended way to make instances is to call [`TestServer::builder`].
13///
14/// # Creating a [`TestServer`]
15///
16/// ```rust
17/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
18/// #
19/// use axum::Router;
20/// use axum_test::TestServer;
21///
22/// let my_app = Router::new();
23/// let server = TestServer::builder()
24///     .save_cookies()
25///     .default_content_type(&"application/json")
26///     .build(my_app);
27/// #
28/// # Ok(())
29/// # }
30/// ```
31///
32#[derive(Debug, Clone)]
33pub struct TestServerBuilder {
34    config: TestServerConfig,
35}
36
37impl TestServerBuilder {
38    /// Creates a default `TestServerBuilder`.
39    pub fn new() -> Self {
40        Default::default()
41    }
42
43    pub fn from_config(config: TestServerConfig) -> Self {
44        Self { config }
45    }
46
47    pub fn http_transport(self) -> Self {
48        self.transport(Transport::HttpRandomPort)
49    }
50
51    pub fn http_transport_with_ip_port(self, ip: Option<IpAddr>, port: Option<u16>) -> Self {
52        self.transport(Transport::HttpIpPort { ip, port })
53    }
54
55    pub fn mock_transport(self) -> Self {
56        self.transport(Transport::MockHttp)
57    }
58
59    pub fn transport(mut self, transport: Transport) -> Self {
60        self.config.transport = Some(transport);
61        self
62    }
63
64    pub fn save_cookies(mut self) -> Self {
65        self.config.save_cookies = true;
66        self
67    }
68
69    pub fn do_not_save_cookies(mut self) -> Self {
70        self.config.save_cookies = false;
71        self
72    }
73
74    pub fn default_content_type(mut self, content_type: &str) -> Self {
75        self.config.default_content_type = Some(content_type.to_string());
76        self
77    }
78
79    pub fn expect_success_by_default(mut self) -> Self {
80        self.config.expect_success_by_default = true;
81        self
82    }
83
84    pub fn restrict_requests_with_http_scheme(mut self) -> Self {
85        self.config.restrict_requests_with_http_scheme = true;
86        self
87    }
88
89    /// Creates a new [`TestServer`], running the application given,
90    /// and with all settings from this `TestServerBuilder` applied.
91    ///
92    /// ```rust
93    /// use axum::Router;
94    /// use axum_test::TestServer;
95    ///
96    /// let app = Router::new();
97    /// let server = TestServer::builder()
98    ///     .save_cookies()
99    ///     .default_content_type(&"application/json")
100    ///     .build(app);
101    /// ```
102    ///
103    /// This is the equivalent to building [`TestServerConfig`] yourself,
104    /// and calling [`TestServer::new_with_config`].
105    ///
106    /// Note: this will panic if the [`TestServer`] cannot be built.
107    /// To catch the error use [`TestServerBuilder::try_build`].
108    pub fn build<A>(self, app: A) -> TestServer
109    where
110        A: IntoTransportLayer,
111    {
112        self.try_build(app)
113            .error_message("Failed to build TestServer")
114    }
115
116    /// Attempts to build a [`TestServer`] from this builder,
117    /// and returns an error if this fails.
118    pub fn try_build<A>(self, app: A) -> Result<TestServer>
119    where
120        A: IntoTransportLayer,
121    {
122        self.into_config().try_build(app)
123    }
124
125    /// For turning this into a [`TestServerConfig`] object,
126    /// with can be passed to [`TestServer::new_with_config`].
127    ///
128    /// ```rust
129    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
130    /// #
131    /// use axum::Router;
132    /// use axum_test::TestServer;
133    ///
134    /// let my_app = Router::new();
135    /// let config = TestServer::builder()
136    ///     .save_cookies()
137    ///     .default_content_type(&"application/json")
138    ///     .into_config();
139    ///
140    /// // Build the Test Server
141    /// let server = TestServer::new_with_config(my_app, config);
142    /// #
143    /// # Ok(())
144    /// # }
145    /// ```
146    pub fn into_config(self) -> TestServerConfig {
147        self.config
148    }
149}
150
151impl Default for TestServerBuilder {
152    fn default() -> Self {
153        Self {
154            config: TestServerConfig::default(),
155        }
156    }
157}
158
159impl From<TestServerConfig> for TestServerBuilder {
160    fn from(config: TestServerConfig) -> Self {
161        TestServerBuilder::from_config(config)
162    }
163}
164
165#[cfg(test)]
166mod test_build {
167    use super::*;
168    use std::net::Ipv4Addr;
169
170    #[test]
171    fn it_should_build_default_config_by_default() {
172        let config = TestServer::builder().into_config();
173        let expected = TestServerConfig::default();
174
175        assert_eq!(config, expected);
176    }
177
178    #[test]
179    fn it_should_save_cookies_when_set() {
180        let config = TestServer::builder().save_cookies().into_config();
181
182        assert_eq!(config.save_cookies, true);
183    }
184
185    #[test]
186    fn it_should_not_save_cookies_when_set() {
187        let config = TestServer::builder().do_not_save_cookies().into_config();
188
189        assert_eq!(config.save_cookies, false);
190    }
191
192    #[test]
193    fn it_should_mock_transport_when_set() {
194        let config = TestServer::builder().mock_transport().into_config();
195
196        assert_eq!(config.transport, Some(Transport::MockHttp));
197    }
198
199    #[test]
200    fn it_should_use_random_http_transport_when_set() {
201        let config = TestServer::builder().http_transport().into_config();
202
203        assert_eq!(config.transport, Some(Transport::HttpRandomPort));
204    }
205
206    #[test]
207    fn it_should_use_http_transport_with_ip_port_when_set() {
208        let config = TestServer::builder()
209            .http_transport_with_ip_port(Some(IpAddr::V4(Ipv4Addr::new(123, 4, 5, 6))), Some(987))
210            .into_config();
211
212        assert_eq!(
213            config.transport,
214            Some(Transport::HttpIpPort {
215                ip: Some(IpAddr::V4(Ipv4Addr::new(123, 4, 5, 6))),
216                port: Some(987),
217            })
218        );
219    }
220
221    #[test]
222    fn it_should_set_default_content_type_when_set() {
223        let config = TestServer::builder()
224            .default_content_type("text/csv")
225            .into_config();
226
227        assert_eq!(config.default_content_type, Some("text/csv".to_string()));
228    }
229
230    #[test]
231    fn it_should_set_expect_success_by_default_when_set() {
232        let config = TestServer::builder()
233            .expect_success_by_default()
234            .into_config();
235
236        assert_eq!(config.expect_success_by_default, true);
237    }
238
239    #[test]
240    fn it_should_set_restrict_requests_with_http_scheme_when_set() {
241        let config = TestServer::builder()
242            .restrict_requests_with_http_scheme()
243            .into_config();
244
245        assert_eq!(config.restrict_requests_with_http_scheme, true);
246    }
247}