alexliesenfeld / httpmock

HTTP mocking library for Rust.
MIT License
436 stars 40 forks source link

Asserting the absence of body, headers, etc #44

Closed ducaale closed 1 year ago

ducaale commented 3 years ago

I would like to assert that my HTTP request body is empty or that a certain header doesn't exist. Is this something that httpmock supports at the moment?

Currently, I can see there the following functions exist:

It would be nice if an opposite version of them existed plus one for checking if the body is empty.

ducaale commented 3 years ago

Actually, there is matches() function that is flexible enough for my current needs

let server = MockServer::start();
let mock = server.mock(|when, then| {
    when.matches(|req: &HttpMockRequest| {
        !req.headers
            .as_ref()
            .map(|headers| headers.iter().any(|(key, _)| key == "authorization"))
            .unwrap_or(false)
    });
    then.body("final destination");
});
alexliesenfeld commented 3 years ago

There is no direct functionality that checks for the absence of attributes at the moment. It could be a valuable addition in the future though. Thanks!

You can indeed use matches() for cases where built-in matcher functions are not sufficient. Just want to note that it's the only matcher function that cannot be used in combination with a standalone mock server, but I think this is not what you are after. Glad you found a solution!

Let's keep this issue opened to track the progress on negators / absence checks.

ducaale commented 3 years ago

Thanks for confirming. I have been trying to add some of the methods I mentioned to httpmock::When via extension traits

trait WhenExt {
    fn header_not_exists(self, name: String) -> Self;
}

impl WhenExt for httpmock::When {
    fn header_not_exists(self, name: String) -> Self {
        self.matches(|req: &HttpMockRequest| {
            !req.headers
                .as_ref()
                .map(|headers| headers.iter().any(|(key, _)| key == &name))
                .unwrap_or(false)
        })
    }
}

Unfortunately, that didn't work because matches doesn't accept closures

error[E0308]: mismatched types
    --> tests/cli.rs:1767:22
     |
1767 |           self.matches(|req: &HttpMockRequest| {
     |  ______________________^
1768 | |             !req.headers
1769 | |                 .as_ref()
1770 | |                 .map(|headers| headers.iter().any(|(key, _)| key == &name))
1771 | |                 .unwrap_or(false)
1772 | |         })
     | |_________^ expected fn pointer, found closure
     |
     = note: expected fn pointer `for<'r> fn(&'r HttpMockRequest) -> bool`
                   found closure `[closure@tests/cli.rs:1767:22: 1772:10]`

So far I didn't find a way around this error. Is it possible to makes matches accept closures in the future?

alexliesenfeld commented 3 years ago

This is a limitation currently that is planned to be changed in the upcoming releases.

akhramov commented 2 years ago

Is it feasible to have a more generic FnMut closure instead of a fn pointer?

The inability to capture local variables is very limiting. I had to use thread_local variables to provide state for the matcher. FWIW my use-case is intermittent requests / retry-behaviour.

Apart from that, a very great library with a pleasant UX. Thank you!

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 1 year ago

This issue was closed because it has been inactive for 14 days since being marked as stale.