pub struct TestApp { /* private fields */ }Expand description
Builder for constructing a fully-configured Autumn application in tests.
Analogous to Spring Boot’s @SpringBootTest – it wires up routes,
middleware, config, and optionally a database pool, then produces a
TestClient ready to fire requests.
§Examples
use autumn_web::prelude::*;
use autumn_web::test::TestApp;
#[get("/ping")]
async fn ping() -> &'static str { "pong" }
#[tokio::test]
async fn ping_works() {
let client = TestApp::new()
.routes(routes![ping])
.build();
client.get("/ping").send().await.assert_ok();
}Implementations§
Source§impl TestApp
impl TestApp
Sourcepub fn policy<R, P>(self, policy: P) -> Self
pub fn policy<R, P>(self, policy: P) -> Self
Register a Policy for
resource type R. Mirrors
AppBuilder::policy.
Sourcepub fn scope<R, S>(self, scope: S) -> Self
pub fn scope<R, S>(self, scope: S) -> Self
Register a Scope for resource
type R. Mirrors
AppBuilder::scope.
Sourcepub const fn forbidden_response(self, value: ForbiddenResponse) -> Self
pub const fn forbidden_response(self, value: ForbiddenResponse) -> Self
Override the deny-response shape used by #[authorize] and
#[repository(policy = ...)] handlers. Useful for
round-tripping the 403-vs-404 decision in tests.
Sourcepub fn openapi(self, config: OpenApiConfig) -> Self
pub fn openapi(self, config: OpenApiConfig) -> Self
Enable OpenAPI spec generation for the test app.
Mirrors crate::app::AppBuilder::openapi so integration tests
can exercise the /v3/api-docs and /swagger-ui endpoints.
Gated behind the openapi Cargo feature.
Sourcepub fn mount_mcp(self, path: impl Into<String>) -> Self
pub fn mount_mcp(self, path: impl Into<String>) -> Self
Mount an MCP endpoint at path, mirroring
AppBuilder::mount_mcp so
integration tests can drive initialize/tools/list/tools/call
through the in-process pipeline.
Gated behind the mcp Cargo feature.
Sourcepub fn expose_all_as_mcp(self) -> Self
pub fn expose_all_as_mcp(self) -> Self
Enable the whole-API MCP hatch, mirroring
AppBuilder::expose_all_as_mcp.
Gated behind the mcp Cargo feature.
Sourcepub fn secure_mcp<L>(self, layer: L) -> Self
pub fn secure_mcp<L>(self, layer: L) -> Self
Gate the entire MCP endpoint behind a tower layer, mirroring
AppBuilder::secure_mcp.
Gated behind the mcp Cargo feature.
Sourcepub fn merge(self, router: Router<AppState>) -> Self
pub fn merge(self, router: Router<AppState>) -> Self
Merge a router into the internal application state.
This is useful when testing modular route definitions without building the full application.
Sourcepub fn scoped<L>(self, prefix: &str, layer: L, routes: Vec<Route>) -> Self
pub fn scoped<L>(self, prefix: &str, layer: L, routes: Vec<Route>) -> Self
Mount routes under a scoped prefix with a route-local layer.
Sourcepub fn nest(self, path: &str, router: Router<AppState>) -> Self
pub fn nest(self, path: &str, router: Router<AppState>) -> Self
Nest a router under a specific path prefix for testing.
This is useful for testing sub-applications or API versions.
Sourcepub fn layer<L: IntoAppLayer>(self, layer: L) -> Self
pub fn layer<L: IntoAppLayer>(self, layer: L) -> Self
Apply a custom tower::Layer to the entire test application.
Mirrors crate::app::AppBuilder::layer so tests can exercise the
exact middleware wiring that AppBuilder::run() produces.
Sourcepub fn with_error_reporter<R: ErrorReporter>(self, reporter: R) -> Self
pub fn with_error_reporter<R: ErrorReporter>(self, reporter: R) -> Self
Register an ErrorReporter for this
test app.
Mirrors crate::app::AppBuilder::with_error_reporter. Call multiple
times to chain reporters; each receives every panic + 5xx event.
Sourcepub const fn idempotent(self) -> Self
pub const fn idempotent(self) -> Self
Enable HTTP idempotency-key middleware for this test app.
Mirrors crate::app::AppBuilder::idempotent: sets the
config.idempotency.enabled flag so that the router wires up the layer
with the same MemoryIdempotencyStore and MetricsCollector that
production uses.
Sourcepub fn from_router(router: Router, state: AppState) -> TestClient
pub fn from_router(router: Router, state: AppState) -> TestClient
Construct a TestClient directly from an axum::Router.
Useful for bypassing TestApp builder if you just want to write requests
against a standard axum Router. The probe state returned by
TestClient::probes will be in the default ready state; it is not
connected to any handler in the supplied router.
Sourcepub fn routes(self, routes: Vec<Route>) -> Self
pub fn routes(self, routes: Vec<Route>) -> Self
Register a collection of routes to be built into the TestApp.
Sourcepub fn state_initializer<F>(self, f: F) -> Self
pub fn state_initializer<F>(self, f: F) -> Self
Register a callback to configure/initialize the application state before building the router.
Sourcepub fn with_flag_store<S>(self, store: S) -> Selfwhere
S: FlagStore,
pub fn with_flag_store<S>(self, store: S) -> Selfwhere
S: FlagStore,
pub fn with_mail_interceptor(self, interceptor: impl MailInterceptor) -> Self
pub fn with_job_interceptor(self, interceptor: impl JobInterceptor) -> Self
pub fn with_db_interceptor( self, interceptor: impl DbConnectionInterceptor, ) -> Self
pub fn with_channels_interceptor( self, interceptor: impl ChannelsInterceptor, ) -> Self
pub fn with_http_interceptor(self, interceptor: impl HttpInterceptor) -> Self
Sourcepub fn config(self, config: AutumnConfig) -> Self
pub fn config(self, config: AutumnConfig) -> Self
Override the default test configuration.
Sourcepub fn with_clock<C>(self, clock: C) -> Selfwhere
C: ClockSource + 'static,
pub fn with_clock<C>(self, clock: C) -> Selfwhere
C: ClockSource + 'static,
Inject a custom clock into the test app.
All handlers that take a crate::time::Clock extractor will see time
as reported by clock. Use crate::time::FixedClock to pin time to
a known instant, or crate::time::TickingClock when you need to step
the clock forward between requests via
TestClient::advance_clock.
use autumn_web::test::TestApp;
use autumn_web::time::{FixedClock, TickingClock};
use chrono::{TimeZone, Utc};
// Pin to a fixed instant:
let _client = TestApp::new()
.with_clock(FixedClock::at(Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap()))
.build();
// Step forward in time:
let clock = TickingClock::starting_at(Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap());
let client = TestApp::new()
.with_clock(clock.clone())
.build();
client.advance_clock(std::time::Duration::from_secs(3600));Sourcepub fn api_version(self, version: ApiVersion) -> Self
pub fn api_version(self, version: ApiVersion) -> Self
Register a single API version for testing.
Sourcepub fn api_versions(
self,
versions: impl IntoIterator<Item = ApiVersion>,
) -> Self
pub fn api_versions( self, versions: impl IntoIterator<Item = ApiVersion>, ) -> Self
Register multiple API versions for testing.
Sourcepub fn with_db(self, pool: Pool<AsyncPgConnection>) -> Self
pub fn with_db(self, pool: Pool<AsyncPgConnection>) -> Self
Attach a database connection pool to the test app.
Sourcepub const fn transactional(self) -> Self
pub const fn transactional(self) -> Self
Enable transactional test isolation using the database URL configured in the application’s configuration.
Sourcepub fn with_transactional_db(self, url: impl Into<String>) -> Self
pub fn with_transactional_db(self, url: impl Into<String>) -> Self
Enable transactional test isolation with an explicit database URL.
Sourcepub fn http_mock(&mut self, alias: &str) -> MockSetupBuilder
pub fn http_mock(&mut self, alias: &str) -> MockSetupBuilder
Register a canned HTTP response for outbound requests made via the
Client extractor during this test.
alias identifies the named service (must match the alias passed to
Client::named in the handler, or
the key used in [http.client.base_urls]).
Returns a MockSetupBuilder on
which you chain the HTTP method and path before calling
respond_with to
register the entry and get a
MockHandle for later assertions.
§Examples
use autumn_web::test::TestApp;
use serde_json::json;
let mut app = TestApp::new();
let mock = app
.http_mock("stripe")
.post("/v1/charges")
.respond_with(200, json!({"id": "ch_123", "amount": 1000}));
let client = app.build();
// … fire requests …
mock.expect_called(1);Sourcepub fn build(self) -> TestClient
pub fn build(self) -> TestClient
Build the application and return a TestClient ready for requests.
This constructs the full Axum router with all middleware applied,
identical to what AppBuilder::run() produces – without binding
a TCP listener.
The process-level global cache is cleared unconditionally so that
#[cached] functions inside this test app always use their
per-function Moka stores and do not accidentally inherit a Redis or
other shared backend installed by a previous test.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for TestApp
impl !Sync for TestApp
impl !UnwindSafe for TestApp
impl Freeze for TestApp
impl Send for TestApp
impl Unpin for TestApp
impl UnsafeUnpin for TestApp
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> RepositoryHooksDefault for Twhere
T: Default,
impl<T> RepositoryHooksDefault for Twhere
T: Default,
Source§fn autumn_default() -> T
fn autumn_default() -> T
Source§impl<T, Conn> RunQueryDsl<Conn> for T
impl<T, Conn> RunQueryDsl<Conn> for T
Source§fn execute<'conn, 'query>(
self,
conn: &'conn mut Conn,
) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
fn execute<'conn, 'query>( self, conn: &'conn mut Conn, ) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
Source§fn load<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn load<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Source§fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
Stream] with the returned rows. Read moreSource§fn get_result<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
fn get_result<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
Source§fn get_results<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn get_results<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Vec with the affected rows. Read more