1

I'm trying to write a simple unit test case for an Actix web function, however I'm getting an error when trying to call the function from the test function. The error I'm getting is: E0618: expected function, found <my function name>

I've tried calling it exactly the way advised on Actix website.

Here's a code sample:

use actix_web::{get, web, Responder, Result};
use serde::Serialize;

#[derive(Serialize, Debug)]
struct PingResponse {
    status: String,
}

#[get("/ping")]
async fn health_check() -> Result<impl Responder> {
    //web::Json<PingResponse> {
    let resp = PingResponse {
        status: "alive".to_string(),
    };

    Ok(web::Json(resp))
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::test;

    #[actix_web::test]
    async fn test_ping_ok() {
        let req = test::TestRequest::default().to_http_request();

        // E0618 expected function, found `health::health_check`
        let resp = health_check(req).await;

        // E0618: expected function, found `health_check`
        // let resp = crate::apis::health::health_check(req);

        assert_eq!(resp.status(), "alive".to_string());
    }
}

I've tried calling the health_check function by just using the function name as well as by using the fully qualified function name.

The diagnostic message is:

error[E0618]: expected function, found `apis::health::health_check`
  --> src/apis/health.rs:29:20
   |
9  | #[get("/ping")]
   | --------------- `apis::health::health_check` defined here
...
29 |         let resp = health_check(req).await;
   |                    ^^^^^^^^^^^^-----
   |                    |
   |                    call expression requires function
Pavel Byles
  • 21
  • 1
  • 6
  • 1
    Are you sure you can directly call these once you've applied the `#[get(...)]` operation on them? My guess is they're promoted to another thing altogether. Remember these macros can *substantially* alter your code before compilation. What you may want to do is set up a server in your test, then make a normal GET request, unless there's a better way to do integration testing. – tadman Nov 29 '22 at 18:26
  • I suspect the ```#[get("/ping")]``` is making it seem like a struct. I think I should be able to call. The compiles and works as expected without the unit tests. – Pavel Byles Nov 29 '22 at 18:57
  • It "works" because Actix is expecting that route to be whatever it is internally, not just a function. The function itself is rewritten and wrapped inside something else. I'm not sure it can be called directly, you'll need to go through the routing layer. It's not that it seems like a struct, it *is* one by the time that macro is done with it. – tadman Nov 29 '22 at 19:01
  • You may be able to see what happened to your code with something like `cargo expand`. – tadman Nov 29 '22 at 19:02
  • 1
    Correct, I can see that the function isn't returning what I think it was, but rather, a struct. I have found a couple solutions - 1) remove the ``` #[get("/ping")] ``` attribute and do the routing from the http server setup. This allows me to call the function normally from the unit test. 2) Use ``` test::TestRequest::get() ``` to make the call generically. This way I can leave the routing on the function. – Pavel Byles Nov 29 '22 at 19:56

1 Answers1

1

I can see that the function isn't returning what I think it was, but rather, a struct. I have found a couple solutions:

  1. Remove the #[get("/ping")] attribute and do the routing from the http server setup. This allows me to call the function normally from the unit test.

  2. Use test::TestRequest::get() and then do app.call(req) to make the call generically. This way I can leave the routing on the function.

Pavel Byles
  • 21
  • 1
  • 6