LukeMathWalker / wiremock-rs

HTTP mocking to test Rust applications.
Apache License 2.0
607 stars 69 forks source link

Integer as JSON Response doesn't work #105

Closed simanacci closed 1 year ago

simanacci commented 1 year ago

Code

[dependencies]
tokio =  { version = "1.23.0", features = [ "full" ] }
wiremock = "0.5.15"
reqwest = { version = "0.11", features = [ "json" ] }
serde = { version = "1.0.147", features = [ "derive" ] }
use serde::{Deserialize, Serialize};
use wiremock::{
    matchers::{self, method},
    Mock, MockServer, ResponseTemplate,
};

#[derive(Serialize, Deserialize, Debug)]
#[serde(transparent)]
struct Max {
    pub max: Option<i32>,
}

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
struct Config {
    pub max_id_url: String,
}

async fn find_max_id_from_hn(config: Config) -> Result<Option<i32>, reqwest::Error> {
    println!("{:#?}", config);
    let max = reqwest::get(&config.max_id_url)
        .await?
        .json::<Max>()
        .await
        .ok();
    Ok(max.max)
}

#[tokio::main]
async fn main() {
    let map = reqwest::get("https://hacker-news.firebaseio.com/v0/maxitem.json?print=pretty")
        .await
        .unwrap()
        .json::<Max>()
        .await
        .unwrap();
    println!("Actual {:?}", map);

    let mock_server = MockServer::start().await;
    let max_id_url = format!("{}/v0/maxitem.json?print=pretty", mock_server.uri());
    let config = Config { max_id_url };
    let body = Max { max: Some(33)};
    let template = ResponseTemplate::new(200).set_body_json(body);
    Mock::given(method("GET"))
        .and(matchers::path("/v0/maxitem.json?print=pretty"))
        .respond_with(template)
        .mount(&mock_server)
        .await;
    let max = find_max_id_from_hn(config).await.unwrap();
    println!("set_body_json {:?}", max);

    let mock_server = MockServer::start().await;
    let max_id_url = format!("{}/v0/maxitem.json?print=pretty", mock_server.uri());
    let config = Config { max_id_url };
    let template = ResponseTemplate::new(200)
        .set_body_raw(r#"34222111"#.as_bytes().to_owned(), "application/json");
    Mock::given(method("GET"))
        .and(matchers::path("/v0/maxitem.json?print=pretty"))
        .respond_with(template)
        .mount(&mock_server)
        .await;
    let max = find_max_id_from_hn(config).await.unwrap();
    println!("set_body_raw {:?}", max);

    let mock_server = MockServer::start().await;
    let max_id_url = format!("{}/v0/maxitem.json?print=pretty", mock_server.uri());
    let config = Config { max_id_url };
    let template = ResponseTemplate::new(200).set_body_string("34222111");
    Mock::given(method("GET"))
        .and(matchers::path("/v0/maxitem.json?print=pretty"))
        .respond_with(template)
        .mount(&mock_server)
        .await;
    let max = find_max_id_from_hn(config).await.unwrap();
    println!("set_body_string {:?}", max);
}

Output

warning: `playground` (bin "playground") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 4.86s
     Running `target/debug/playground`
Actual Max { max: Some(34189447) }
Config {
    max_id_url: "http://127.0.0.1:35255/v0/maxitem.json?print=pretty",
}
set_body_json None
Config {
    max_id_url: "http://127.0.0.1:45337/v0/maxitem.json?print=pretty",
}
set_body_raw None
Config {
    max_id_url: "http://127.0.0.1:43199/v0/maxitem.json?print=pretty",
}
set_body_string None

Actual result from Hacker News API https://i.imgur.com/79SJnTX.png

LukeMathWalker commented 1 year ago

It's an issue with your matcher - it should be:

    Mock::given(method("GET"))
        // We do NOT include query parameters here
        .and(matchers::path("/v0/maxitem.json"))
        .and(matchers::query_param("print", "pretty"))
        .respond_with(template)
        // [...]

104 should make this more obvious going forward.

LukeMathWalker commented 1 year ago

If you update to wiremock 0.5.16 and run your code again, you'll get this error:

thread 'main' panicked at 'Wiremock can't match the path `/v0/maxitem.json?print=pretty` because it contains a `?`. You must use `wiremock::matchers::query_param` to match on query parameters (the part of the path after the `?`).'

which should be more helpful in nudging you in the right direction.