Mockaco

website·Indexed 30 days ago

https://natenho.github.io/Mockaco/

A tool to simplify the setup and execution of API mocks featuring Pure C# scripting, built-in fake data generation, and support for stateful mocks. It includes capabilities for mocking binary/raw and XML responses, request matching, chaos engineering, and a generator for creating mock templates from specifications like OpenAPI.

Tokens
8.1K
Snippets
54
Records
66
Agent score
73%

What's inside Mockaco

  1. Overview of Mockaco API Mocking

    Mockaco simplifies the setup and execution of API mocks. It allows developers to add dynamic content to mocks using pure C# scripting, eliminating the need to learn a proprietary language or specific API for mock logic. Additionally, it includes built-in fake data generation to streamline the creation of mock responses.
  2. Understand Mockaco request matching logic

    When a request is received, Mockaco determines which mock file to use based on the following process:

    1. Mock files are searched in alphabetical order.
    2. The request is compared against the request object criteria in each file.
    3. The first match that meets the criteria is selected. If multiple mocks match, those with a condition are prioritized.
    4. If no match is found, Mockaco returns an HTTP 501 (Not Implemented) response containing a list of possible file parsing errors.
  3. Create a mock response in Mockaco

    To create a mock in Mockaco, create a JSON file within the Mocks folder. The file defines a request/response template: when the server receives a request matching the specified method and route, it will return the defined status and response body.
    {
      "request": {
        "method": "GET",
        "route": "ping"
      },
      "response": {
        "status": "OK",
        "body": {
          "response": "pong"
        }
      }
    }
  4. Query the request body using JSONPath

    For complex objects, use the SelectToken (returns a single JToken) and SelectTokens (returns a collection of JTokens) methods on Request.Body to filter and traverse data using JSONPath. If a token is not found, these methods return null.
    {
      "request": {
        "method": "POST",
        "route": "/json_path"
      },
      "response": {
        "body": {
          "Manufacturer": "<#= Request.Body.SelectToken("Manufacturers[0].Name") #>",
          "Price": "<#= Request.Body.SelectToken("Manufacturers[0].Products[0].Price") #>",
          "ProductName": "<#= Request.Body.SelectToken("Manufacturers[1].Products[0].Name") #>",
          "AcmeManufacturer": <#= Request.Body.SelectToken("$.Manufacturers[?(@.Name == 'Acme Co')]") #>,
          "FilteredProducts": <#= JsonConvert.SerializeObject(Request.Body.SelectTokens("$..Products[?(@.Price >= 50)].Name")) #>
        }
      }
    }
  5. Mock XML responses using request data

    Mockaco can parse XML request payloads and make their elements available for use in response or callback templates. To ensure correct parsing, the Content-Type header must be set to application/xml or text/xml.

    Accessing XML Data

    Use the following syntax within response templates to access request body elements:

    • Simple elements (no attributes): Access directly by name. Example: Request.Body["root"]?["theSongName"]
    • Elements with attributes (text content): Use the #text key to get the inner text. Example: Request.Body["root"]?["theAlbum"]?["#text"]
    • Element attributes: Use the @ prefix followed by the attribute name. Example: Request.Body["root"]?["theAlbum"]?["@year"]
    {
      "request": {
        "method": "POST",
        "route": "songs"
      },
      "response": {
        "status": "OK",
        "headers": {
          "Content-Type": "application/xml"
        },
        "body": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<song>\n  <name><#=Request.Body[\"root\"]? [\"theSongName\"]#></name>\n  <album>\n    <name><#=Request.Body[\"root\"]? [\"theAlbum\"]? [\"#text\"]#></name>\n    <year><#=Request.Body[\"root\"]? [\"theAlbum\"]? [\"@year\"]#></year>\n  </album>\n</song>"
      }
    }
  6. Run Mockaco from source code

    Clone the repository and run the project using the .NET CLI. If the --urls parameter is not provided during execution, a random local port will be chosen.
    $ git clone https://github.com/natenho/Mockaco.git
    $ cd Mockaco\src\Mockaco
    $ dotnet run
  7. Generate mock templates from a specification

    Use the mockaco generate command to automatically create mock templates based on a provided specification file or URL. You must specify the provider (e.g., openapi) to tell Mockaco how to parse the specification.
    mockaco generate [url/path to the specification] --provider=[provider name]
    
    # Example: Generating mocks from the Petstore OpenAPI specification
    mockaco generate https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json --provider=openapi
  8. Install and run Mockaco using .NET CLI

    Install Mockaco as a global .NET tool. You can specify a custom URL using the --urls parameter; otherwise, a random local port will be assigned automatically.
    $ dotnet tool install -g mockaco
    $ mockaco --urls "http://localhost:5000"
  9. Simulate stateful API behavior using the Global object

    Because the Global object is shared across all requests, you can store a variable in one mock endpoint and retrieve it in another. This is useful for simulating stateful interactions (e.g., a 'store' endpoint that sets a value and a 'retrieve' endpoint that reads it).
    // Mock 1: Store the variable
    {
      "request": {
        "method": "GET",
        "route": "store"
      },
      "response": {
        "status": "OK",
        "body": {
          "response": "This request stores a variable"
        }
      }
      <#
        Global["my-custom-variable"] = "hello!";
      #>
    }
    
    // Mock 2: Retrieve the variable
    {
      "request": {
        "method": "GET",
        "route": "retrieve"
      },
      "response": {
        "status": "OK",
        "body": {
          "response": "The variable is <#=Global["my-custom-variable"] #>"
        }
      }
    }
  10. Generate fake data using the Faker facade

    Mockaco provides a Faker facade object that can be used within script tags (<#= ... #>) to generate random fake data for response bodies. Common categories include Name, Company, and Address.
    {
      "request": {
        "method": "GET"
      },
      "response": {
        "status": "OK",
        "headers": {
          "Content-Type": "application/json"
        },
        "body": {
          "name": "<#= Faker.Name.FullName() #>",
          "company": "<#= Faker.Company.CompanyName() #>",
          "city": "<#= Faker.Address.City() #>"
        }
      }
    }
  11. Extract data from the request body as a dictionary

    The Request.Body property is a JToken object containing the parsed request body. Mockaco supports application/json, application/xml, text/xml, text/plain, application/x-www-form-urlencoded, and multipart/form-data content types.

    You can query the body using dictionary-style indexing. Because missing keys return null, it is recommended to use the C# null-conditional operator (?).

    {
      "response": {
        "body": {
            "id": "<#=Request.Route["id"] #>",
            "name": "<#=Request.Body["songs"]?[0]?["name"] #>",
            "album": {
                "name": "<#=Request.Body["songs"]?[0]?["album"]?["name"] #>",
                "year": "<#=Request.Body["songs"]?[0]?["album"]?["year"] #>"
            }
        }
      }
    }
  12. Manage global state in Mockaco mocks

    Mockaco allows persisting state using a Global object, which consists of simple global variables stored in memory and accessible across all active mocks. These variables can hold any object, such as boolean flags, strings, or request payloads. If a global variable has not been initialized, it returns null.
    // Setting a global variable (wrapped in <# #>)
    <#
        Global["my-custom-variable"] = "my-custom-state";
    #>
    
    // Retrieving a global variable (wrapped in <#= #>)
    <#=Global["my-custom-variable"]#>