LukeMathWalker / wiremock-rs

HTTP mocking to test Rust applications.
Apache License 2.0
617 stars 70 forks source link

Mocking External APIs ? #81

Closed iot-resister closed 2 years ago

iot-resister commented 2 years ago

Hi,

Let's say my api calls another api like so

#[get("/title/1")]
pub async fn get_title() -> Result<impl Responder> {
    let res = reqwest::Client::new()
        .get("https://jsonplaceholder.typicode.com/posts/1")
        .send()
        .await
        .map_err(|e| actix_web::error::ErrorInternalServerError(e))?;

    let json = res
        .json::<Resp>()
        .await
        .map_err(|e| actix_web::error::ErrorInternalServerError(e))?;

    Ok(json.title)
}

when testing get_title , can I mock/intercept the call to https://jsonplaceholder.typicode.com/posts/1 ?

LukeMathWalker commented 2 years ago

Hey! We do not manipulate name resolution, so I won't work as it is. The usual strategy is to make the base URL a configuration parameter/pass it as an argument inside your function. In this way, you can pass the base Uri of the mock server in tests which allows you to test the behaviour :)

simanacci commented 1 year ago

I'm trying to mock a request to https://hacker-news.firebaseio.com/v0/user/{username}.json?print=pretty but it actually fetches from firebaseio.com. How should I specify the uri?

let body = format!(
    r#"
{{
    "about" : "This is a test {}",
    "created" : 1173923446,
    "delay" : 0,
    "id" : {},
    "karma" : 2937,
    "submitted" : [ 8265435, 8168423, 4289 ]
}}
"#,
    hex, new_hn_username
);
let mock_server = MockServer::start().await;
let template = ResponseTemplate::new(200).set_body_raw(body, "application/json");
// let hnpath = format!("https://hacker-news.firebaseio.com/v0/user/{}.json?print=pretty", new_hn_username);
let hnpath = format!("/v0/user/{}.json?print=pretty", new_hn_username);
Mock::given(method("GET"))
    .and(matchers::path(&hnpath))
    .respond_with(template)
    .mount(&mock_server)
    .await;
LukeMathWalker commented 1 year ago

You need to use the mock server uri:

let mock_server = MockServer::start().await;
let hnpath = format!("{}/v0/user/{}.json?print=pretty", mock_server.uri(), new_hn_username);
simanacci commented 1 year ago

It's still fetching from firebaseio.com. Here's my setup, is it supposed to work for such? https://gist.github.com/simanacci/67c2f5a096e1db2012503cc7133c25f0

LukeMathWalker commented 1 year ago

You need to set https://gist.github.com/simanacci/67c2f5a096e1db2012503cc7133c25f0#file-wiremock-rs-L47 to the mock server URI. There is no DNS magic, if you use the firebase URL you will hit firebase.

simanacci commented 1 year ago

I think it works but my response is empty instead of body. Err(reqwest::Error { kind: Decode, source: Error("EOF while parsing a value", line: 1, column: 0) })

let body = HNResponse {
    about: Some(format!("This is a test {}", hex)),
    created: 1173923446,
    delay: None,
    id: format!("{}", new_hn_username),
    karma: 2444,
    submitted: Some(v),
};

let template = ResponseTemplate::new(200).set_body_json(body);
Mock::given(method("GET"))
    .and(matchers::path(&hnpath)) // "http://127.0.0.1:45685/v0/user/9sqsFVl.json?print=pretty"
    .respond_with(template)
    .mount(&mock_server)
    .await;
LukeMathWalker commented 1 year ago

The path matcher should not include the URL base. I.e. it should be /user for http://hackernews.com/user.

simanacci commented 1 year ago

It works. Thank you.