Test behavior at two boundaries
A successful HTTP response is only one part of application correctness. You also need to know that the service enforces its rules when called without HTTP, and that invalid input does not mutate state.
First test the operation
Construct the service directly and call the same operation your handler calls. For a create operation, check at least:
- Valid input returns the new value and makes it retrievable.
- Blank or otherwise invalid input returns a validation error.
- Failed validation leaves storage unchanged.
- Looking up an absent ID returns a not-found error.
The notes application's tests show this using its real memory-backed service. These tests do not need an HTTP server or JSON. That is the practical payoff of keeping HTTP-specific types out of the operation.
Then test the adapter
Axum's Router is a Tower service. You can submit an in-process request with
tower::ServiceExt::oneshot instead of binding a TCP port. Here is a complete
small adapter test; dependencies are iet-core, iet-http-axum, Axum 0.8, Serde
with derive, Tokio with macros and a runtime, and Tower with util.
use ;
use ;
use ;
use ;
use ServiceExt;
async
async
This test was executed successfully against the current local framework crates; the validation input and configuration definitions on the adjacent reference pages were also compiled in that isolated check. This does not validate every snippet on the site.
This isolated test intentionally has no limiter or persistence. It tests the extractor's behavior, not deployment admission control or database correctness. The real application's router needs separate tests for its configured middleware.
For authenticated endpoints, add missing credentials, expired sessions, forbidden access, and cross-user resource access. A valid login followed by a successful request does not establish ownership isolation.
Use a real backend when testing backend behavior
An in-memory substitute cannot prove SQL predicates, transaction rollback, migration checksums, or database constraints. Run those against a disposable TimescaleDB instance. Avoid pointing destructive tests at a developer's shared database. The persistence guide explains where database sessions enter an operation.
Know what a passing command proves
Run the tutorial's cargo test --manifest-path command to test its application.
That is different from cargo test --workspace at the repository root, which
also exercises unrelated framework components and architecture rules. The current
root core-architecture test has a known conflict with the boxed health-probe
registry. A focused application test passing does not mean that full suite passed.
Likewise, a documentation-site build checks page syntax and links, not Rust code inside Markdown fences. Only examples explicitly accompanied by an executed compile/test command should be described as tested.