lipanski / mockito

HTTP mocking for Rust!
MIT License
695 stars 59 forks source link

Delay mock response #160

Closed sr-gi closed 1 year ago

sr-gi commented 1 year ago

I've been using .with_body_from_fn to add delays to some of my mock responses as suggested in https://github.com/lipanski/mockito/issues/64#issuecomment-463403179. e.g:

let api_mock = server
    .mock("POST", endpoint))
    .with_status(400)
    .with_header("content-type", "application/json")
    .with_body_from_fn(|w| {
        thread::sleep(Duration::from_secs_f64(API_DELAY));
        w.write_all(
            json!(ApiError {
                error: "error_msg".to_owned(),
                error_code: 1,
            })
            .to_string()
            .as_bytes(),
        )
    })
    .create_async()
    .await;

However, I've noticed that when tests are run in parallel as part of a bigger suite, the elapsed time for the delays is bigger than expected. This does not happen if the test is run standalone or if tests are run using -test-threads=1.

So that poses two questions:

lipanski commented 1 year ago

since you're using async code there, try tokio::time::sleep instead - though you'd have to wrap the contents of the closure in async. or you could move the sleep to a tokio::task::spawn_blocking block, not sure.

sr-gi commented 1 year ago

I've tried to use tokio::time::sleep, but it doesn't look like async can be used in the with_body_from_fn closure (or I'm not creating this correctly). Would you mind providing a working example of an async sleep inside a with_body_from_fn block?

lipanski commented 1 year ago

I couldn't figure out the reason for the delay. I'll have a closer look when implemeting with_body_from_request.

lipanski commented 1 year ago

what works right now is using the sync API:

        let mut s = mockito::Server::new();
        let _m = s.mock("GET", "/").with_body_from_fn(|writer| {
            std::thread::sleep(std::time::Duration::from_secs(5));
            writer.write_all(b"test")
        }).create();

...but you'd have to change those tests using the sync API to be fully sync.

sr-gi commented 1 year ago

I don't think I can actually. This is a pretty simple example, but the tests are using some async components that are actually the ones sending the request to the mock.

I have a workaround in place for now, so I guess I'll wait until the async equivalent is in place.

Thanks for the help :)

lipanski commented 1 year ago

@sr-gi this should work with the Mock::with_body_from_request callback introduced in 0.32.4. I tried it out with the std::thread::sleep function and it seemed to respect the provided durations.

sr-gi commented 1 year ago

@sr-gi this should work with the Mock::with_body_from_request callback introduced in 0.32.4. I tried it out with the std::thread::sleep function and it seemed to respect the provided durations.

I'm getting the same exact issue as before:

lipanski commented 1 year ago

@sr-gi the difference seems quite small (except for the multithreaded scenario), which is probably why I didn't spot it. if that's a problem then I'd recommend again the sync scenario. otherwise you can have a look under the hood yourself, I'd accept a pull request if you can figure out the issue :smile_cat:

sr-gi commented 1 year ago

@sr-gi the difference seems quite small (except for the multithreaded scenario), which is probably why I didn't spot it. if that's a problem then I'd recommend again the sync scenario. otherwise you can have a look under the hood yourself, I'd accept a pull request if you can figure out the issue 😸

Yeah, the two first scenarios are actually correct, the small difference may come from how I'm actually accounting for the delays. It is the multithreaded case that breaks my tests.

I'll try to see if there is a way of implementing a variant of with_body_from_request or with_body_from_fn that accepts async closures.