Module pyo3_asyncio::testing
source · Expand description
testing
Utilities for writing PyO3 Asyncio tests
PyO3 Asyncio Testing Utilities
This module provides some utilities for parsing test arguments as well as running and filtering a sequence of tests.
As mentioned here, PyO3 Asyncio tests cannot use the default test harness since it doesn’t allow Python to gain control over the main thread. Instead, we have to provide our own test harness in order to create integration tests.
Running pyo3-asyncio
code in doc tests is supported however since each doc test has its own
main
function. When writing doc tests, you may use the
#[pyo3_asyncio::async_std::main]
or
#[pyo3_asyncio::tokio::main]
macros on the test’s main function to run
your test.
If you don’t want to write doc tests, you’re unfortunately stuck with integration tests since
lib tests do not offer the same level of flexibility for the main
fn. That being said,
overriding the default test harness can be quite different from what you’re used to doing for
integration tests, so these next sections will walk you through this process.
Main Test File
First, we need to create the test’s main file. Although these tests are considered integration
tests, we cannot put them in the tests
directory since that is a special directory owned by
Cargo. Instead, we put our tests in a pytests
directory.
The name
pytests
is just a convention. You can name this folder anything you want in your own projects.
We’ll also want to provide the test’s main function. Most of the functionality that the test harness needs is packed in the pyo3_asyncio::testing::main
function. This function will parse the test’s CLI arguments, collect and pass the functions marked with #[pyo3_asyncio::async_std::test]
or #[pyo3_asyncio::tokio::test]
and pass them into the test harness for running and filtering.
pytests/test_example.rs
for the tokio
runtime:
#[pyo3_asyncio::tokio::main]
async fn main() -> pyo3::PyResult<()> {
pyo3_asyncio::testing::main().await
}
pytests/test_example.rs
for the async-std
runtime:
#[pyo3_asyncio::async_std::main]
async fn main() -> pyo3::PyResult<()> {
pyo3_asyncio::testing::main().await
}
Cargo Configuration
Next, we need to add our test file to the Cargo manifest by adding the following section to the
Cargo.toml
[[test]]
name = "test_example"
path = "pytests/test_example.rs"
harness = false
Also add the testing
and attributes
features to the pyo3-asyncio
dependency and select your preferred runtime:
pyo3-asyncio = { version = "0.13", features = ["testing", "attributes", "async-std-runtime"] }
At this point, you should be able to run the test via cargo test
Adding Tests to the PyO3 Asyncio Test Harness
We can add tests anywhere in the test crate with the runtime’s corresponding #[test]
attribute:
For async-std
use the pyo3_asyncio::async_std::test
attribute:
mod tests {
use std::{time::Duration, thread};
use pyo3::prelude::*;
// tests can be async
#[pyo3_asyncio::async_std::test]
async fn test_async_sleep() -> PyResult<()> {
async_std::task::sleep(Duration::from_secs(1)).await;
Ok(())
}
// they can also be synchronous
#[pyo3_asyncio::async_std::test]
fn test_blocking_sleep() -> PyResult<()> {
thread::sleep(Duration::from_secs(1));
Ok(())
}
}
#[pyo3_asyncio::async_std::main]
async fn main() -> pyo3::PyResult<()> {
pyo3_asyncio::testing::main().await
}
For tokio
use the pyo3_asyncio::tokio::test
attribute:
mod tests {
use std::{time::Duration, thread};
use pyo3::prelude::*;
// tests can be async
#[pyo3_asyncio::tokio::test]
async fn test_async_sleep() -> PyResult<()> {
tokio::time::sleep(Duration::from_secs(1)).await;
Ok(())
}
// they can also be synchronous
#[pyo3_asyncio::tokio::test]
fn test_blocking_sleep() -> PyResult<()> {
thread::sleep(Duration::from_secs(1));
Ok(())
}
}
#[pyo3_asyncio::tokio::main]
async fn main() -> pyo3::PyResult<()> {
pyo3_asyncio::testing::main().await
}
Lib Tests
Unfortunately, as we mentioned at the beginning, these utilities will only run in integration tests and doc tests. Running lib tests are out of the question since we need control over the main function. You can however perform compilation checks for lib tests. This is much more useful in doc tests than it is for lib tests, but the option is there if you want it.
my-crate/src/lib.rs
mod tests {
use pyo3::prelude::*;
#[pyo3_asyncio::async_std::test]
async fn test_async_std_async_test_compiles() -> PyResult<()> {
Ok(())
}
#[pyo3_asyncio::async_std::test]
fn test_async_std_sync_test_compiles() -> PyResult<()> {
Ok(())
}
#[pyo3_asyncio::tokio::test]
async fn test_tokio_async_test_compiles() -> PyResult<()> {
Ok(())
}
#[pyo3_asyncio::tokio::test]
fn test_tokio_sync_test_compiles() -> PyResult<()> {
Ok(())
}
}
Structs
- Args that should be provided to the test program
- The structure used by the
#[test]
macros to provide a test to thepyo3-asyncio
test harness.
Functions
- Parses test arguments and passes the tests to the
pyo3-asyncio
test harness - Parse the test args from the command line
- Run a sequence of tests while applying any necessary filtering from the
Args