LukeMathWalker / wiremock-rs

HTTP mocking to test Rust applications.
Apache License 2.0
639 stars 73 forks source link

async `respond_with` / `Respond` #84

Open johannescpk opened 2 years ago

johannescpk commented 2 years ago

Thanks for wiremock! It seems that it's currently not possible to execute async code in the respond method or respond trait. It'd be useful to have it natively supported by wiremock.

Wasn't able to find a workaround, tried

.respond_with(move |req: &Request| {
        let handle = tokio::runtime::Handle::current();
    std::thread::spawn(move || {
        handle.block_on(async {
            // Some async call
        });
    })
    .join()
    .unwrap();

    ResponseTemplate::new(200)
})

but that's blocking the tokio runtime since the test runs as #[tokio::test].

LukeMathWalker commented 2 years ago

What kind of async tasks are you trying to perform in response_with?

johannescpk commented 2 years ago

It's some business logic, in this case it's tower::util::Oneshot calls.

LukeMathWalker commented 2 years ago

I'll need an example a bit more concrete to understand if it makes sense to extend the library or not 😄

johannescpk commented 2 years ago

Personally I'd say it'd make sense to have callbacks be async in general unless there's a good reason to not do it. Reason being that it's easy to have a sync wrapper in an async context but it's hard the other way around.

In this specific case I'm testing the correct setup of a FHIR Subscription. The HTTP request flow looks like this. The relevant part is 9. and 10. where a handshake takes place.

Basically:

Client --[ HTTP POST Create Subscription ]--> Server
Server --[ HTTP POST Handshake ] --> Client

The library exposing the functionality has an async method create_subscription which should only return once the subscription was created in full, with a successful handshake. To test this I have two MockServer instances, one being the client and one being the server. The test then calls create_subscription and only after the subscription mock finished (the server mock returning the ResponseTemplate) it should do a mocked handshake call to the client so create_subscription returns. The communication inside the client to signal that the handshake arrived is made with tokio::Notify. In the tests I'm now also using Notify as a workaround (requires me to juggle a tokio::join! later in the test), but I'd like to directly use https://github.com/tokio-rs/axum/blob/main/examples/testing/src/main.rs#L64-L67 which is async.

rminderhoud commented 1 year ago

Hey there, thanks for this great library! I recently ran into the same issue. Trying to call some async code from a custom responder. Basically, I am trying to create a mock to validate an HTTP payload and one of the libraries to parse multipart data is async. e.g.

struct CustomResponder;

impl wiremock::Respond for CustomResponder {
    fn respond(&self, req: &wiremock::Request) -> wiremock::ResponseTemplate {
        // ... some logic ...

        // This is an async function so can't be used here
        let multipart = multer::Multipart::with_reader(req.body.as_slice(), boundary);

        // ... validate multipart data ...
    }

A lot of http libraries have gone async so it would be useful to be able to use them

moh-eulith commented 1 year ago

Just another example in case it helps: In the response method, I have to call another service (another mock which implements json rpc), which is async.

bnjbvr commented 1 week ago

Wanted to share my 2c and use case, that I posted on the wrong repo initially lol.

First of all, thanks for this project, it's quite useful for us over at the Matrix Rust SDK project.

There's one thing that I miss and would like to see: the ability to set async custom responders. Right now, it's possible to respond_with a static ResponseTemplate, or a sync closure Fn(&Request) -> ResponseTemplate. I'd like to be able to also use something that returns a future there, and for wiremock to handle it for me.

This would allow for new functionality that I'd also like to be able to use, and that as far as I can't tell doesn't exist (but that would be great to prove me wrong :))