Getting started

You will of course want to use stubr in your Rust project. We will cover here the case where you want to mock an external http application you do not own (if you own it, you might be interested in contract testing).

First you need a stub. A stub is a json file which represents the endpoint you want to mock. You have 2 options from now on:

We are going to be even lazier and simply create the json stub with a command.

You should have a project layout like this:

├── src
│   ├── lib.rs
└── tests
    ├── stubs
├── Cargo.toml
├── README.md

We are going to create the stub under tests/stubs/, the default location. You can place them wherever you want of course, but you'll see it's more convenient to place them here.

echo "{\"request\": {\"method\": \"GET\", \"urlPath\": \"/hello\"}, \"response\": { \"body\": \"Hello stubr\" }}" > tests/stubs/hello.json

And with a few lines of code we can spawn a mock server and call (here with reqwest for example).

use asserhttp::*;

#[tokio::test]
async fn getting_started() {
    // run a mock server with the stub 👇
    let stubr = stubr::Stubr::start("tests/stubs/hello.json").await;
    // or use 'start_blocking' for a non-async version

    // the mock server started on a random port e.g. '127.0.0.1:43125'
    // so we use the stub instance 'path' (or 'uri') method to get the address back
    let uri = stubr.path("/hello");
    reqwest::get(uri).await
        // (optional) use asserhttp for assertions
        .expect_status_ok()
        .expect_content_type_text()
        .expect_body_text_eq("Hello stubr");
}

But we can further shorten this with a attribute macro: #[stubr::mock]

use asserhttp::*;

#[tokio::test]
#[stubr::mock("hello.json")] // 👈 this starts the mock server
async fn getting_started() {
    // a local binding 'stubr' has been created, equivalent to the one before
    let uri = stubr.path("/hello");
    reqwest::get(uri)
        .await
        .expect_status_ok()
        .expect_content_type_text()
        .expect_body_text_eq("Hello stubr");
}

You can also use the macro in non-async test methods of course, the macro will adapt by itself.
Note that here you can omit the tests/stubs/ path prefix. If you placed your files in the default location, they are going to be searched from there.
As well, you can mount many stubs with the macro e.g. #[stubr::mock("hello.json", "goodbye.json")]. By default, if you take care to place only stubs with different request matching under tests/stubs, you can simply place #[stubr::mock]. It will recursively mount all the stubs under tests/stubs, searching also in subdirectories.

Here are all the options you can use with the attribute macro

#[tokio::test]
#[stubr::mock(full_path = "tests/book/hello.json", port = 1234, verify = true)]
async fn getting_started() {}
  • full_path: use this if your stubs are not under tests/stubs but elsewhere. Note that it can point to a directory.
  • port when you want an explicit port for your mock server
  • verify to turn on verification of the number of times a stub gets called (expect field in your stubs). See simulating fault for reference