-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to use a global runtime for all tests? #2374
Comments
I would suggest that using a global connection pool like that is generally an anti pattern and generates surprising effects. I personally would suggest removing the global pool and using the same tokio::test macro. This is by far the best way to reduce tests affecting each other in weird ways. |
@LucioFranco In addition, in this particular case, the application is a web server that uses postgres as the storage layer and, of course, it internally manages all accesses to the DB through a connection pool. |
So if you have a global runtime you can just in your tests do RT.block_on(async move {
// test
}) which is basically what the macro does but because of certain internals block_on takes &mut self, so you will want to do the Handle::current + futures_executor::block_on trick. Overall, I still suggest spinning up a single connection pool per test anyways, I don't suspect this should be too expensive if you only need say one connection per test? |
@LucioFranco pub fn test<F: std::future::Future>(f: F) -> F::Output {
rt().handle().enter(|| futures::executor::block_on(f))
}
#[test]
fn a_test() {
test(async {
println!(" this is async");
});
} Anyway, it would be great to have it on tokio itself so there would be no need for all that boilerplate code and the nested "test()" call. |
I think we are likely to not support something like that since we generally want to promote the usage of |
I believe I have run into a scenario where I am truly unable to use a per-test runtime as set up by I am writing a Discord bot. Discord's API is large and complex, so I can't mock it. I also can't run it locally, because it is proprietary. So, my tests must run against the live API. Let's call the type that represents a session with the Discord API as a Discord rate-limits log in to once every 5 seconds. So I can't create a new I tried to use a global I'm not sure, but I've seen some surprising errors, like this one, which I believe are being caused by running futures on the wrong runtime. So, I think I have no choice but to use a global runtime. I understand the desire to guide users towards |
See: tokio-rs/tokio#2374 The tokio::test tests each run their own runtime. If we want to share the server between tests, the easiest way seems to be to not use the tokio::test annotation, and instead run our own tests with a shared runtime.
I ran into another situation where I was forced to use a global runtime for all tests. The tests in question were browser tests, which use the Any chance this issue could get a second look? So far, it's been a pretty common (2 for 2) that I've needed to use a global runtime when writing async tests, so I suspect it's common for others. It would be nice if there was an easy way to do this, without needing to use something like #[test]
fn test() {
RUNTIME.block_on(async {
// test code
});
} In every test. |
I am open to suggestions for how to achieve a shared runtime. |
How about a
|
I'm doing something similar to @ufoscout as shown below, but not sure I understand what's happening. In the code below I'm lazy initializing a static variable RT by creating a new runtime and then running some async setup code inside of the rt.block_on call. This code runs as expected and blocks until its async closure completes entirely. However the subsequent function run_test has a call test_rt().block_on, which is what I use to execute my actual tests, and these calls return immediately without waiting for the internal future to complete. What is the difference between the two calls, why does the first one wait and the other does not?
|
I ran into this problem too and created this crate to provide https://crates.io/crates/tokio-shared-rt #[tokio_shared_rt::test]
async fn t1() {
db_pool().foo().await;
}
#[tokio_shared_rt::test(shared)]
async fn t2() {
db_pool().foo().await;
}
#[tokio_shared_rt::test(shared = true)]
async fn t3() {
db_pool().foo().await;
}
#[tokio_shared_rt::test(shared = false)]
async fn delicate_runtime_test() {
db_pool().foo().await;
} |
I'd love to hear what the problems are preventing just sticking it in a |
I am also facing a recent testing issue that would benefit from Tokio supporting a parameter to enable a shared runtime between tests. I am surprised that this feature has not already been implemented given how commonly it seems to be needed for a variety of use cases. |
This reverts part of commit 4a2fe0c and fixes the test instability in a better way as tipped off by @AmeliasCode in [this comment](hyperium/tonic#1751 (comment)) and specifically shared by @gwy15 in [this comment](tokio-rs/tokio#2374 (comment)). It seems the problem was that after the first test finished, the runtime that 'owned' the Mutex or channel was shut down, breaking it for all subsequent users. By changing our tests to all use a shared runtime, the channel isn't killed and can therefore work in subsequent tests.
I ran into a similar issue. static KEYCLOAK: OnceCell<(ContainerAsync<GenericImage>, String)> = OnceCell::const_new();
async fn start_auth_container() -> &'static (ContainerAsync<GenericImage>, String) {
KEYCLOAK
.get_or_init(|| async { start_keycloak_container().await })
.await
}
#[tokio::test]
async fn test1(
) {
let keycloak = start_auth_container().await;
println!("{:?}", keycloak.1);
//let app = start_app("some").await;
}
#[tokio::test]
async fn test2(
) {
let keycloak = start_auth_container().await;
println!("{:?}", keycloak.1);
//let app = start_app("some").await;
} A shared runtime, where I could do some specific clean up call at the end, would be a great solutions and other testing frameworks like junit have such stuff for years... |
Is it possible to use the same runtime instance to run all tests?
The use case:
An instance of my application is created and stored in a
lazy_static
object to be used by all tests; this application internally uses an async connection pool based on tokio.The problem:
[tokio::test]
creates a new runtime for each test, so:To solve this I would like
[tokio::test]
to use a single runtime instance for all tests.For example, an idea could be:
The text was updated successfully, but these errors were encountered: