After a lot of reading and practicing exercises, I'm now writing my first real-world Rust program, a testing utility for a website search engine.
I've started out prototyping with reqwests. The easy way to do this is using the RequestBuilder, but I'm a TDD guy and would like to write my tests without out-of-process dependencies. I've come up with the code below (not a test, just rewritten as simply as possible) for exercising an endpoint. The main thing I struggled with is setting the body.
Any suggestions and criticism are welcome!
use reqwest::Url;
use reqwest::blocking::{Body, Request};
fn main() {
let params = vec![("teeth", "pointy"), ("venom", "deadly")];
let url = Url::parse_with_params("http://reptiles.com", params).unwrap();
let mut request = Request::new(reqwest::Method::POST, url);
let body = request.body_mut();
*body = Some(Body::from("The body content".to_string()));
}
But then I need another client to actually execute it:
let request = Request::try_from(test_case).unwrap();
let response = Client::new().execute(request).unwrap();
I think normally you'd call send(). That would be out of process. I could implement TryFrom for RequestBuilder instead, test that and then call build().send(). That would be quite acceptable really.
One thing I'm not sure I got right in the code above is this:
let body = request.body_mut();
*body = Some(Body::from("The body content".to_string()));
I'm still new at this, and wasn't entirely sure how to handle that part. It certainly seems less ergonomic than the RequestBuilder version. I just saw a type hint for mut &Option<Body> and hacked it.
Maybe you could pass the Client as part of TestCase? Then you wouldn't have to construct a new one in TryFrom::try_from. Storing the client as a global variable in a LazyLock would also be an option.
* is Rust's dereference operator. Your snippet does update the body of the request. I agree with your observation that RequestBuilder offers the nicer API though.
It would be better to double-down on using RequestBuilder, and return unbuilt RequestBuilder to the caller, instead of a finalized Request.
BTW: you're saying out-of-process, but it's unclear what you mean by that. For the meaning of process as an operating system process (running executable), it all stays within a single process.
The trait implementation might be a bit overkill. In my case it might be better to have a SearchClient with a method that produces RequestBuilders from TestCases.