httpmock icon indicating copy to clipboard operation
httpmock copied to clipboard

Asserting the absence of body, headers, etc

Open ducaale opened this issue 4 years ago • 5 comments

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:

  • header_exists()
  • query_params_exists()
  • cookie_exists()

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

ducaale avatar May 13 '21 16:05 ducaale

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");
});

ducaale avatar May 13 '21 19:05 ducaale

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.

alexliesenfeld avatar May 13 '21 19:05 alexliesenfeld

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?

ducaale avatar May 13 '21 19:05 ducaale

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

alexliesenfeld avatar May 13 '21 21:05 alexliesenfeld

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!

akhramov avatar Feb 14 '22 13:02 akhramov

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

github-actions[bot] avatar Apr 18 '23 01:04 github-actions[bot]

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

github-actions[bot] avatar May 02 '23 01:05 github-actions[bot]