Code Monkey home page Code Monkey logo

http-mock-player's Introduction

Build Status

HTTP Mock Player

The library implements recorder and player of HTTP requests. When recording, a request to a remote service and its live response are serialized to JSON and saved to a file (cassette), so that next time the same request is made through the player it can be served with the previously recorded response. This scenario is common for integration tests running HTTP requests in isolation.

Installation

PM> Install-Package HttpMockPlayer

Supported .NET versions

The library is built on top of .NET Standard 2.0.

Usage

The following example uses NUnit testing framework together with the HttpMockPlayer library to test a remote API:

...
MyAPIclient client;
Player player;
Cassette cassette;

[OneTimeSetUp]
public void OneTimeSetUp()
{
    var baseAddress = new Uri("http://localhost:5555/");
    var remoteAddress = new Uri("https://api.myserver.com");

    client = new MyAPIclient(baseAddress);
    
    player = new Player(baseAddress, remoteAddress);
    player.Start();
    
    cassette = new Cassette("/path/to/mock/api-calls.json");
    player.Load(cassette);
}

[OneTimeTearDown]
public void OneTimeTearDown()
{
    player.Close();
    client.Close();
}

[SetUp]
public void SetUp()
{
    var record = TestContext.CurrentContext.Test.Name;
    
    if (cassette.Contains(record))
    {
        player.Play(record);
    }
    else
    {
        player.Record(record);
    }
}

[TearDown]
public void TearDown()
{
    player.Stop();
}

[Test]
public void GetStuff_ReturnsStuff()
{
    var res = client.GetStuff();
    
    Assert.IsNotNull(res);
    ...
}

[Test]
public void CreateStuff_Unauthorized_Throws()
{
    ...
    Assert.Throws<HttpRequestException>(() => client.CreateStuff(param1, param2));
}

Once run, this code generates a JSON file (api-calls.json as specified in the cassette constructor):

[
  {
    "name": "GetStuff_ReturnsStuff",
    "requests": [
      {
        "request": {
          "method": "GET",
          "uri": "https://api.myserver.com/stuff",
          "headers": {
            "Connection": "Keep-Alive",          
            "Host": "api.myserver.com",
            "User-Agent": "MyAPIclient/1.0"
          }
        },
        "response": {
          "statusCode": 200,
          "statusDescription": "OK",
          "content": "{\"id\":\"a77a6649-1d41-4d80-a763-494640b99a4b\",
          \"items\":[]}",
          "headers": {
            "Status": "200 OK",
            "X-Served-By": "a123456f3b2fa272558fa6dc951018ad",
            "Content-Length": "56",
            "Cache-Control": "public, max-age=60, s-maxage=60",
            "Content-Type": "application/json; charset=utf-8",
            "Date": "Tue, 28 Jun 2016 20:06:06 GMT",
            "Last-Modified": "Tue, 28 Jun 2016 00:40:50 GMT",
            "Server": "myserver.com"
          }
        }
      }
    ]
  },
  {
    "name": "CreateStuff_Unauthorized_Throws",
    "requests": [
    {
      "request": {
        "method": "POST",
        "uri": "https://api.myserver.com/stuff",
        "content": "{\"id\":\"186b0d73-4ab6-4726-a963-85996944b6b4\",
        \"items\":[]}",
          "headers": {
            "Content-Length": "56",
            "Content-Type": "application/json; charset=utf-8",
            "Expect": "100-continue",
            "Host": "api.myserver.com",
            "User-Agent": "MyAPIclient/1.0"
          }
        },
        "response": {
          "statusCode": 401,
          "statusDescription": "Unauthorized",
          "content": "{\"message\":\"Requires authentication\"}",
          "headers": {
            "Status": "401 Unauthorized",
            "Content-Length": "37",
            "Content-Type": "application/json; charset=utf-8",
            "Date": "Tue, 28 Jun 2016 20:06:07 GMT",
            "Server": "myserver.com"
          }
        }
      }
    ]
  }
]

The Player object is initialized with the (local) base address, where it listens for client requests, and an address of a remote service, to which the requests are redirected when recording. Note that the player serves as a forward proxy and does not intercept HTTP requests, so the client should send its requests to the player's base address in order for them to be recorded or replayed. Remote address can be changed while the player runs, which makes it possible to test distributed web services.

Check the HttpMockPlayer.Samples project for more examples.

Player lifecycle

After initialization the player should be started, so that it can accept incoming requests. It is also required to load a cassette - JSON file where requests will be read from or written to. If the file doesn't exist, it will be created automatically as soon as recording is stopped.

Once started and loaded, the player can play or record a series of requests (a record). If it is already in operation, it won't switch to another record until stopped.

var record = "CreateNewReport";
    
if (cassette.Contains(record))
{
    player.Play(record);
}
else
{
    player.Record(record);
}
...
// authentication request 
// request to fetch additional data
// request to post a new report

player.Stop();

When all done the player should be shut down:

player.Close();

Serialization to JSON

A HTTP request is recorded as JSON object which stores its HTTP method, resource URI, body data and headers:

"request": {
  "method": "POST",
  "uri": "https://api.test.com/entities/1",
  "content": "entity1",
  "headers": {
    "Connection": "Keep-Alive",
    "Content-Length": "7",
    "Cookie": "cookie1=value1; cookie2=value2",
    "Host": "api.github.com",
    "User-Agent": "SampleGithubClient/1.0"
  }
}

For a HTTP response its status code, status description, body data and headers are saved:

"response": {
  "statusCode": 200,
  "statusDescription": "Ok",
  "content": "<!DOCTYPE html><html></html>",
  "headers": {
    "Connection": "keep-alive",
    "Content-Length": "28",
    "Cache-Control": "no-cache,no-store,max-age=0,must-revalidate",
    "Content-Type": "text/html; charset=UTF-8",
    "Date": "Thu, 07 Jul 2016 01:06:49 GMT",
    "Expires": "Thu, 07 Jul 2016 01:06:50 GMT",
    "Last-Modified": "Thu, 07 Jul 2016 01:06:50 GMT",
    "Set-Cookie": "yp=1470445610.ygu.1; Expires=Sun, 05-Jul-2026 01:06:49 GMT; Domain=.ya.ru; Path=/",
    "Server": "nginx",
    "X-Frame-Options": "DENY"
  }
}

Exceptions

In certain cases Player object throws custom exceptions:

  • CassetteException if a cassette cannot be read or saved;
  • PlayerStateException if player is not in a valid state to start an operation (for instance, when Play() is not followed by Stop(), but instead another Play() is called)

If an error occurs while processing a client request, the player wraps it into the response message and sets the status code to 454 (request mismatch), 551 (play exception), 552 (record exception) or 550 (general player exception).

Dependencies

http-mock-player's People

Contributors

igudkova avatar genne avatar

Stargazers

Carl avatar

Watchers

 avatar  avatar

Forkers

adz jointrine

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.