GitHub Logo

Build Status

NuGet

codecov

What is FakeApi?

FakeApi provides the ability to send HttpWebRequest and get HttpWebResponses without a server.

When use FakeApi?

FakeApi is for you!

Oldest versions documentations

v1.3.0 Release note

How to use FakeApi?

FakeApi configuration file

First of all add to your project a json configuration file named as you like.

In this file, you can define default values for all properties available in HttpWebResponse .Net and object than will be use by FakeApi to build responses when you are sending HttpWebRequest. These values will be used by default for all apis that you will configure in this file and must be declare at the root path like bellow:

{
  "defaultDelay": 500,
  "defaultHttpCode": 200,
  "defaultMethod": "GET"
}

Now, you can define your web api configuration:

"apis": [
    {
      "url": "http://localhost:8080/api/users/{idUser}",
      "responses": [
        {
          "active": 1,
          "delay": 300,
          "httpCode": 200,
          "method": "GET",
          "response": "{ 'firstname': 'john', 'lastname':'doe', 'id': 567 }"
        },
        {
          "active": 1,
          "delay": 300,
          "httpCode": 200,
          "method": "PUT",
          "response": "{ 'firstname': 'johnny', 'lastname':'doedoe', 'id': 567 }"
        },
        {
          "active": 0,
          "delay": 300,
          "httpCode": 200,
          "method": "GET",
          "response": "{ 'firstname': 'bob', 'lastname':'dylan', 'id': 567 }"
        },
        {
          "active": 0,
          "delay": 300,
          "httpCode": 200,
          "method": "PUT",
          "response": "{ 'firstname': 'bobby', 'lastname':'dylann', 'id': 567 }"
        }
      ]
    }

For each web api you can set several actives responses. One per pair url/method. At runtime you can switch between responses with changing active property value. Note that you can use template segment in your url configuration (/{idUser}). You can also override all default values defined at the root path in each apis configuration.

You can configure FakeApi in a single file. But you can now split your Apis configurations files into multiple files. To do that, just provide all directories where FakeApi can find api configurations files by this way :

{
  "defaultDelay": 250,
  "defaultHttpCode": 200,
  "defaultMethod": "GET",
  "apisDirectories": [
      "Config/Api/User"
  ]
}

In summary, I advise you as in the example below :

Config example

/!\ Be careful, directories containing api configuration files (such as usersApi.cfg) must not contain any other files! Otherwise FakeApi will try to deserialize them and an exception will be thrown.

Simply set the “file” property into your api configuration:

{
      "url": "http://localhost:8080/api/get-file",
      "responses": [
        {
          "active": 1,
          "method": "GET",
          "httpCode": 200,
          "file": "getResponse.txt"
        }
      ]
    }

If you want to return different data each time you call one of your APIs, then you can define several result files :

{
  "url": "https://localhost/api/users?pIndex={0}&pSize={1}",
  "responses": [
    {
      "active": 1,
      "files": [
        "Config/Api/User/Response/searchUsersPage0.json",
        "Config/Api/User/Response/searchUsersPage1.json",
        "Config/Api/User/Response/searchUsersPage2.json"
      ]
    }
  ]
}

At the first call the data of the first file will be returned then the second to the second call etc … This type of configuration can be useful for paged requests.

You can add cookies and headers into HttpWebResponse:

{
      "url": "http://localhost:8080/api/orders/{orderId}/addresses/{addressId}}",
      "responses": [
        {
          "active": 1,
          "delay": 135,
          "httpCode": 200,
          "cookies": [
            {
              "name": "cookie1",
              "value": "valCookie1",
              "comment": "Comment1",
              "discard": 0,
              "domain": "domain1",
              "expired": 1,
              "expires": "2012-10-12",
              "httpOnly": 1,
              "path": "path1",
              "secure": 1,
              "version": 54
            },
            {
              "name": "cookie2",
              "value": "valCookie2"
            }
          ],
          "headers": [
            {
              "name": "header1",
              "value": "valHeader1"
            },
            {
              "name": "header2",
              "value": "valHeader2  "
            }
          ],
          "response": "{ 'orderId': 345, 'streetAddress':'2762 Highland Drive', 'city': 'Nina' }"
        }

Maybe you will have to test how your code reacts when a web exception is throwing. To do that, you need to set the “webExceptionMessage” property:

{
      "url": "http://localhost:8080/api/ad/{adId}",
      "responses": [
        {
          "active": 1,
          "httpCode": 400,
          "webExceptionMessage": "Invalid ad id"
        }
      ]
}

You can force FakeApi to throw your own exceptions:

{
      "url": "http://localhost:8080/api/ad/{adId}/custom-error",
      "responses": [
        {
          "active": 1,
          "httpCode": 500,
          "customApiException": 
          {
            "fullTypeName": "App.CustomWebException, App",
            "constructorArgs": [
              "custom exception message"
            ]
          }
        }
      ]
}

Finally you have to create your web request and use the FakeHttpRequester provided by FakeApi to get the corresponding HttpWebResponse:


var serviceCollection = new ServiceCollection();

#if DEBUG
serviceCollection.AddScoped<IHttpRequester, FakeHttpRequester>(provider =>
{
    return new FakeHttpRequester("api.cfg.json");
});
#endif

var serviceProvider = serviceCollection.BuildServiceProvider();
var httpRequester = serviceProvider.GetService<IHttpRequester>();

//Get user request
var getUserRequest = (HttpWebRequest)WebRequest.Create("http://localhost:8080/api/users/56");
var getUserResponse = httpRequester.GetResponse(getUserRequest);

using (var stream = new StreamReader(getUserResponse.GetResponseStream()))
{
    var data = stream.ReadToEnd();
    var user = JsonConvert.DeserializeObject<User>(data);
    Console.WriteLine($"json data from {getUserRequest.RequestUri}");
    Console.WriteLine($"Firstname : {user.Firstname} | Lastname : {user.Lastname} | Id : {user.Id}");
}

In production, replace the FakeHttpRequester implementation by DefaultHttpRequester provided by FakeApi. Or you can also write your own implementation of IHttpRequester interface.


/// <summary>
/// Provides method to sending a web request
/// </summary>
public interface IHttpRequester
{
    /// <summary>
    /// Gets the web response for <paramref name="request"/>
    /// </summary>
    HttpWebResponse GetResponse(HttpWebRequest request);

    /// <summary>
    /// Gets the web response async for <paramref name="request"/>
    /// </summary>
    Task<HttpWebResponse> GetResponseAsync(HttpWebRequest request);
}

public class DefaultHttpRequester: IHttpRequester
    {
        public HttpWebResponse GetResponse(WebRequest request)
        {
            if(request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var response = request.GetResponse();
            return response as HttpWebResponse;
        }

        public async Task<HttpWebResponse> GetResponseAsync(WebRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var response = await request.GetResponseAsync();
            return response as HttpWebResponse;
        }
    }

List of available default HttpWebResponse properties

List of available properties for each HttpWebResponse configuration (for overriding default properties)

List of available properties for HttpWebResponse header

Single file Json configuration full example


{
  "defaultDelay": 500,
  "defaultHttpCode": 200,
  "defaultMethod": "GET",
  "apis": [
    {
      "url": "http://localhost:8080/api/users/{idUser}",
      "responses": [
        {
          "active": 1,
          "delay": 300,
          "response": "{ 'firstname': 'john', 'lastname':'doe', 'id': 567 }"
        }
      ]
    },
    {
      "url": "http://localhost:8080/api/orders/{orderId}/addresses/{addressId}}",
      "responses": [
        {
          "active": 1,
          "delay": 135,
          "cookies": [
            {
              "name": "cookie1",
              "value": "valCookie1"
            },
            {
              "name": "cookie2",
              "value": "valCookie2"
            }
          ],
          "headers": [
            {
              "name": "header1",
              "value": "valHeader1"
            },
            {
              "name": "header2",
              "value": "valHeader2  "
            }
          ],
          "response": "{ 'orderId': 345, 'streetAddress':'2762 Highland Drive', 'city': 'Nina' }"
        },
        {
          "active": 0,
          "delay": 5000,
          "httpCode": 203,
          "response": "{ 'orderId': 345, 'streetAddress':'2762 Highland Drive', 'city': 'Nina' }"
        }
      ]
    },
    {
      "url": "http://localhost:8080/api/ad/{adId}",
      "responses": [
        {
          "active": 1,
          "httpCode": 400,
          "webExceptionMessage": "Invalid ad id"
        }
      ]
    },
    {
      "url": "http://localhost:8080/api/ad/{adId}/custom-error",
      "responses": [
        {
          "active": 1,
          "httpCode": 500,
          "customApiException": 
          {
            "fullTypeName": "App.CustomWebException, App",
            "constructorArgs": [
              "custom exception message"
            ]
          }
        }
      ]
    },
    {
      "url": "http://localhost:8080/api/get-file",
      "responses": [
        {
          "active": 1,
          "file": "DownloadFile.txt"
        }
      ]
    }
  ]
}

Project example

You can find a full project example under Example solution folder