4
votes

It seems one cannot use #[tokio-test] for test async functions in the Rust doc test?

For now I have to write an async main function and tag it with #[tokio-main] and call test_fn().await in it to let some async function run during cargo test --doc.

Is there some other better ways to allow doc tests run as if it is a normal test function, for example with #[tokio-test]? Also it would be nice if the tag #[tokio-test] can be shown in the doc, so users can copy the doc and use it as a test directly in their project. (And this probably can be done like ##[tokio-test]?)

2

2 Answers

2
votes

If you want to use asynchronous code in doctests without spawning a new runtime for each you can just wrap the whole doctest instead of just the async part in a tokio_test::block_on.

Example:

/// Asynchronously get length of "aaaa"
/// ```
/// # tokio_test::block_on(async {
///   let len = async { "aaaa".len() };
///   assert_eq!(len.await, 4)
/// # })
/// ```
2
votes

Doc tests automatically wrap the code block in a synchronous fn main() { … }. .awaiting requires an asynchronous runtime. You cannot .await without spawning some sort of a runtime. You could spawn a regular, multi-threaded runtime for each doc test:

/// ```rust
/// #[tokio::main]
/// async fn main() {
///    let len = async { "aaaa".len() }.await;
///    assert_eq!(len, 4);
/// }
/// ```

Spawning a runtime for each doc test is probably not the best idea. A better way would be to use tokio_test::block_on which uses a test local runtime (similar to #[tokio-test]) to block on the provided future:

/// ```rust
/// let len = tokio_test::block_on( async { "aaaa".len() } );
/// assert_eq!(len, 4);
/// ```

To reduce the boilerplate, you can a macro for block_on:

macro_rules! bo {
  ($e:expr) => {
    tokio_test::block_on($e)
  };
}

And use it like so:

/// ```rust
/// let len = async { "aaaa".len() };
/// assert_eq!(bo!(len), 4)
/// ```