6

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]?)

Fuyang Liu
  • 1,496
  • 13
  • 26

3 Answers3

8

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 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);
/// }
/// ```

..but that's 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
/// # tokio_test::block_on(async {
/// let len = async { "aaaa".len() }.await;
/// assert_eq!(len, 4);
/// # }
/// ```
double-beep
  • 5,031
  • 17
  • 33
  • 41
Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54
  • 1
    Thank you. The issue I have is that I would like to use doc to show how to write async code tests with `#[tokio-test]`. But if `#[tokio-test]` cannot be used with doc, then I might not able to write a doc that can show how to use `#[tokio-test]` and also make the doc's code run during cargo test. – Fuyang Liu Nov 24 '20 at 09:56
5

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)
/// # })
/// ```
Raspberry1111
  • 78
  • 3
  • 4
  • Thank you. This seems to be a good solutioon. – Fuyang Liu Mar 02 '21 at 13:37
  • Note that in the current (0.4.2) version of `tokio_test::block_on()`, this will create a new runtime, so it's roughly equivalent to `# tokio::runtime::Handle::new().unwrap().block_on(async {` – Simon Buchan Jun 02 '21 at 08:26
-1

I do not know enough about your special use case but maybe you want to avoid the async runtime and wrap your documentation code block like this:

/// ...
/// Example
/// ```rust,no_run // <- try using this `no_run` tag
/// ...
/// #[tokio::main]
/// async fn main() {
/// ...
/// }
/// ...
/// ```