Test with async

I am writing a gRPC client and want to test server functionality through the client for acceptance testing. At first I got a message that test does not work with async functions. I circumvented the error with the following code.

...
#[test]
fn test_authorize_for_unknown_protobuf() {
#[allow(unused_must_use)]
test1();
}

async fn test1() {
let mut client = grpc_connect().await;

...

But I found out that when debugging under #test, a breakpoint before calling test1 occurs but a breakpoint inside test1 never happens.

Is this correct? Does Rust ignore the inner call because it is async?

In Rust an async function is syntax sugar for a function that returns an object that implements the future trait. Those objects then get run by an executor (like tokio or smol). You drop that object immediately, that is also the reason why you get the unused_must_use warning and don't even start executing the async function.

The easiest way to do this is probably to start an executor (maybe pollster, which is probably the simplest async executor) in your test and pass it the async function you want to test.
I don't know how good the debugger experience will be. In async the compiler does a lot of stuff to the functions before they get into the binary and then the executor does a lot of stuff as well, so it may look weird.

You almost got the formatting right. But the right characters are not periods but instead your choice is between:

  • “backticks” aka free-standing grave accents

    ```
    code here
    ```
    
  • or tilde marks

    ~~~
    code here
    ~~~
    

The exact method of inputting these depends on your language / keyboard; choose whichever is easier to produce :wink:

Thanks for the tip, I'll give pollster a try and see if it works.

I tried to get the formatting right. It looked like ... on my screen.

That’s understandable, and why I’ve added some explanations what the right choice of character would have been! The forum rendering actually uses a form of markdown syntax.

If you don’t wrap code sections in such a block, it can lead to confusing display, as e.g.

•  indentation/spacing is not preserved

for example the code

fn foobar() {
    println!("hello       world!");
}

looks like this without the code block:

fn foobar() {
println!("hello world!");
}

•  HTML-like syntax can be interpreted as HTML and then rendered as such, or discarded if disallowed

for example the code

fn my_function<Generic>() {
    let pi = 3.14;
    let reference = &pi;
}

looks like this without the code block:

fn my_function() {
let pi = 3.14;
let reference = π
}

If you remove the #[allow(unused_must_use)] you put there you'll get a warning explaining exactly what's the problem:

warning: unused implementer of `Future` that must be used
 --> src/lib.rs:3:1
  |
3 | test1();
  | ^^^^^^^
  |
  = note: futures do nothing unless you `.await` or poll them
  = note: `#[warn(unused_must_use)]` on by default

Note this part:

  = note: futures do nothing unless you `.await` or poll them

You're not doing any of these, so the future does nothing, i.e. the code inside the async function is never executed. To have it do anything you need to run the future with an executor, similarly to how you would do this in main.

3 Likes

If you're using tokio, they have a test macro for this.

1 Like

I tried your suggestion. It did't work. Failed

aused by:
  process didn't exit successfully: `/Users/bihaber/chat/rust/registry/registry/target/debug/deps/reg_client-92df80484634fb94 --format=json -Z unstable-options --show-output` (exit status: 231)
note: test exited abnormally; to see the full output pass --nocapture to the harness.
error: 1 target failed:
    `-p registry --bin reg-client`

Process finished with exit code 101

I'll have to do further investigation but it seems the best solution if I can make it work, Thanks for the tip.

Show the modified code along with the error please.

My bad. I reran it after restarting server and it worked.

Would you mind marking the reply that helped you to solve the problem as the solution? That's the convention to allow others to find the solution, and it gives the poster credit, which is kind of a nice thank you.

The problem was my own. The client failed because the server was stopped. There was nothing wrong with the code.

Oh, I'm sorry. So you didn't use the test macro to solve your original problem?

I used the Tokio test and it worked great.

Tokio test worked great but suddenly it stopped working and build always failed with No 'main' function found in crate.
If I add 'main' none of the tests run. Sometimes it does not fail with that error but it is intermittent. So far I have not found what is causing the problem.

Do you have harness = false somewhere in your Cargo.toml file? That would cause that combination of errors.

I assume that would be a Tokio option? If not where?

I mean literally in your Cargo.toml. If it is not set then that isn't the problem.

To troubleshoot further, can you please post the full output of a cargo test command that gives the "No 'main' function found in crate." error?

I have gotten past my original problem, it wasn't running cargo test. But now when I run it recompiles all dependencies which makes it take a long time to get to any tests.