#http #web-server #web #testing-http #http-client #spec #api

http_stub

Actual web servers, testing the full stack. Assert on the request, and send a response back

4 releases

Uses old Rust 2015

0.1.3 Jun 17, 2016
0.1.2 Apr 22, 2016
0.1.1 Apr 22, 2016
0.1.0 Apr 21, 2016

#37 in #testing-http

21 downloads per month
Used in 2 crates

WTFPL license

8KB
79 lines

HttpStub

Ad-hoc local servers to test your HTTP client code.

Actual web servers,
testing the full stack.
Assert on the request,
and send a response back.

It's based on hyper,
because it's the best.
And asserts with regex,
instead of just text.

The code is quite simple,
and easy to digest,
if you want to contribute,
send your pull request.

Installation

[dependencies]
http_stub = "0.1"

What does it look like?

See actual examples in the docs


lib.rs:

HttpStub: Ad-hoc local servers help you test your HTTP client code.

Easily define as many stub servers as you want. Make assertions on the request and build a response to send back.

Fork on GitHub

Examples

extern crate http_stub;
extern crate hyper;

// Your client HTTP code will likely be using hyper too so
// this is the recommended way to use http_stub to avoid
// name clashing.
use self::http_stub as hs;
use self::http_stub::HttpStub;

// These modules are for the actual code we're writing and testing.
use std::io::Read;
use hyper::client::Client;
use hyper::status::StatusCode;

fn body_to_string<R: Read>(mut readable: R) -> String{
  let ref mut body = vec![];
  let _ = readable.read_to_end(body);
  String::from_utf8_lossy(body).into_owned()
}

fn main(){
  // Run an HttpStub server. It returns the URL where the server is listening,
  // for example: http://127.0.0.1:3001
  // It's fixed to listen on 127.0.0.1 and it will use up ports counting up from
  // port 3000, each new server will use the next port to make sure there are
  // no port conflicts. This does mean you should not be using those ports too.
  let server_one: String = HttpStub::run(|mut stub|{
    stub.got_body(r"foo=bar");
    stub.got_path("/a_post");
    stub.got_method(hs::Method::Post);
    stub.send_status(hs::StatusCode::NotFound);
    stub.send_header(hs::header::ContentType(
      hs::Mime(hs::TopLevel::Application, hs::SubLevel::Json, vec![])));

    // send_body should always be the last step. It writes the response body and sends it.
    // Rendering the 'response' field of the HttpStub unusable.
    stub.send_body("number one");
  });

  let server_two = HttpStub::run(|mut stub|{
    // Notice all search strings are actually used for creating a regex.
    // That's why we escape the '?' when matching for the path.
    stub.got_path(r"/a_get\?foo=bar");
    stub.got_method(hs::Method::Get);
    stub.send_status(hs::StatusCode::Ok);
    stub.send_header(hs::header::ContentType(
      hs::Mime(hs::TopLevel::Application, hs::SubLevel::Json, vec![])));
    stub.send_body("number two");
  });

  let client = Client::new();

  let response_one = client.post(&format!("{}/a_post", server_one))
    .body("foo=bar").send().unwrap();

  assert_eq!(response_one.status, StatusCode::NotFound);
  assert_eq!(body_to_string(response_one), "number one");

  let response_two = client.get(&format!("{}/a_get?foo=bar", server_two))
    .send().unwrap();

  assert_eq!(response_two.status, StatusCode::Ok);
  assert_eq!(body_to_string(response_two), "number two");
}

Dependencies

~9.5MB
~209K SLoC