alexliesenfeld / httpmock

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

Mocking multipart uploads #39

Closed ayrat555 closed 3 years ago

ayrat555 commented 3 years ago

It seems the library fails on multipart/form-data uploads. It works fine with regular requests with json body.

Requests to real server upload files without any issues. I'm using ureq as HTTP client:

    fn request_with_form_data<T1: serde::ser::Serialize, T2: serde::de::DeserializeOwned>(
        &self,
        method: &str,
        params: T1,
        parameter_name: &str,
        file_path: PathBuf,
    ) -> Result<T2, ureq::Error> {
        let json_string = serde_json::to_string(&params).unwrap();
        let json_struct: Value = serde_json::from_str(&json_string).unwrap();

        let mut form = Multipart::new();
        for (key, val) in json_struct.as_object().unwrap().iter() {
            if key != parameter_name {
                let val = match val {
                    &Value::String(ref val) => format!("{}", val),
                    etc => format!("{}", etc),
                };

                form.add_text(key, val);
            }
        }

        let file = std::fs::File::open(&file_path).unwrap();
        let file_extension = file_path.extension().and_then(|s| s.to_str()).unwrap_or("");
        let mime = mime_guess::from_ext(&file_extension).first_or_octet_stream();

        form.add_stream(
            parameter_name,
            file,
            file_path.file_name().unwrap().to_str(),
            Some(mime),
        );

        let url = format!("{}/{}", self.api_url, method);
        let form_data = form.prepare().unwrap();

        let response = ureq::post(&url)
            .set(
                "Content-Type",
                &format!("multipart/form-data; boundary={}", form_data.boundary()),
            )
            .send(form_data)?;

        let parsed_response: T2 = serde_json::from_reader(response.into_reader()).unwrap();

        Ok(parsed_response)
    }

mock:

        let server = MockServer::start();

        server.mock(|when, then| {
            when.method(POST).path(path);
            then.status(200).body(response);
        });
alexliesenfeld commented 3 years ago

Thanks for creating this issue. Multipart uploads are currently not supported but are far up on the todo list.

ayrat555 commented 3 years ago

Ok. Thanks. I think it should be mentioned somewhere.

I just tested my example with mockito. It works fine

alexliesenfeld commented 3 years ago

@ayrat555: You linked the Java version of mockito obove. Did you maybe mean the Rust mockito library? Because AFAICS, mockito (Rust) does not mention multipart support anywhere in the docs either.

I just tested your code locally, but it successfully ran through. Could you maybe provide a reproducable example with the problem you have?

My version is as follows:

#[test]
fn multipart_body_test() {
    #[derive(serde::Serialize, serde::Deserialize)]
    struct ExampleStruct {
        my_key: String,
    }

    // Arrange
    let server = MockServer::start();

    let mock = server.mock(|when, then| {
        when.method("POST").path("/hello");
        then.status(200).json_body_obj(&ExampleStruct{my_key: "my value".into()});
    });

    let path = get_test_data_path("tests/resources/static_yaml_mock.yaml");
    let res : Result<ExampleStruct, ureq::Error> = request_with_form_data(
        "hello",
        json!({ "my_key" : "my value" , "anotherKey": "some other value" }),
        "my_key",
        path,
        &server.base_url()
    );

    mock.assert();
    assert_eq!(res.is_ok(), true);
    assert_eq!(res.unwrap().my_key, "my value");
}
ayrat555 commented 3 years ago

You linked the Java version of mockito obove. Did you maybe mean the Rust mockito library? Because AFAICS, mockito (Rust) does not mention multipart support anywhere in the docs either.

Lol. you're right :)

Here's my examples:

alexliesenfeld commented 3 years ago

Should be fixed with PR #40 in v0.5.8. I have successfully tested your repo with it but please feel free to reopen this issue if it still fails on you for some reason.

StarpTech commented 2 years ago

Hi, is it also possible to match binary content? body is from type String. It would be great to have a helper method when.body_from_file that reads a file from the file system. This would be congruent with then.body_from_file.