There is a time during the development of a project when a developer needs to test the interaction with an external API service. Maybe he needs to get predefined responses to his API calls. In this case, the solution is not to use the original API, but instead to build a mock of said API.

There are many ways to mock http APIs, usually by writing a web server that listens to the same endpoints as the API being mocked. Although this can be simple enough to do on some programming languages, there is an even simpler way to achieve the same results without having to write a single line of code: By using MMock.

MMock is a fast and simple mock http/https server which allows the definition of responses with a simple configuration file. It also has some handy features that I am going to describe shortly.

In this article I am going to use MMock 2.7.9.

Install and Run MMock

There are two ways to install and run MMock:

1. As a docker container, either using the pre-built docker image directly or extending it in your own Dockerfile, and then starting the container by running the command:

docker run -v <PATH_TO_CONFIG_FOLDER>:/config -p 8082:8082 -p 8083:8083 <YOUR_IMAGE_NAME>

2. By running MMock directly on your machine. You need Go 1.8 or higher installed, and the MMock module:

go get github.com/jmartin82/mmock/...
mmock -config-path <PATH_TO_CONFIG_FOLDER>

You should see something like this when running MMock:

The mock http server is accessible at http://localhost:8083/, https at https://localhost:8084/, and a handy MMock console, where you can track configurations and API calls, is accessible at http://localhost:8082/.

Now, before you start sending requests to your mock API, firstly you need to configure MMock.

Configuration Options

To configure an endpoint on your mock API you must create a configuration file for MMock.

The configuration file can be written in .json or .yaml, and should be placed inside the <PATH_TO_CONFIG_FOLDER> specified in the MMock run command.

The following is an exhaustive list of all the options MMock recognizes:

  • description: A small description for the current configuration. This will show up in the MMock console and is useful if you have lots of configurations
  • request: This group of options informs MMock about what kind of requests to expect. All options, except for method, allow global matching with *. All options must match for this configuration to be triggered
    • method: http method (GET, POST, PUT, etc). Multiple methods can be separated by |
    • path: url endpoint/path, http://localhost/8083/<path>
    • body: request body
    • queryStringParameters: array of url query string parameters, http://localhost/8083/endpoint?key=value
    • headers: array of http request headers
    • cookies: array of request cookies
    • host: host from where the request should originate
  • response: This group of options specifies the response that MMock should return
    • statusCode: http status code (200, 404, etc)
    • body: response body
    • headers: array of http response headers
    • cookies: array of http response cookies
  • callback: This group of options informs MMock that it should perform a request to an external API (will be introduced in a future release)
    • method: http method
    • url: API url to call
    • delay: delay before sending request
    • timeout: time to wait for response
    • headers: array of headers
    • body: http body
  • control: This group of options specifies some mechanisms that control some extra behavior of MMock
    • pripority: specifies a value that defines the priority of this configuration over others
    • proxyBaseURL: url to where MMock should send the received request. MMock then returns the response back
    • delay: how long MMock should wait before responding
    • crazy: randomly returns an http 500 error or 200 OK
    • scenario: This option allows MMock to retain some state
      • name: arbitrary scenario name
      • requiredState: the required state of the scenario for this configuration to take effect
      • newState: the new state of the scenario after this configuration takes effect
    • webHookURL: url that will receive a webhook from MMock

The only really mandatory configuration options are the request.method and request.path, although a configuration file with just those two options does nothing.

Example Configurations

Now some examples of what I think are relevant configurations, starting with a simple one that expects a request to /hello and returns a response with http code 200:

description: hello
request:
  method: GET
  path: "/hello"
response:
  statusCode: 200
  body: Hello!

With a configuration file as simple as this we have an endpoint which will always return a 200 OK response, no matter the request’s http body, headers, or cookies!

Now an example of a configuration that expects some query string parameters:

request:
  method: GET
  path: "/hello"
  queryStringParameters:
    user:
      - *
response:
  statusCode: 200
  body: Hello {{request.query.user}}!

For this configuration to trigger, the mock API must be accessed with the user query parameter, like so: /hello?user=john. In this case the response will include the query parameter value! As you might have noticed, the curly braces in the body option define variables, and in this configuration that variable is the value defined in the user query parameter.

The same kind of configuration is applied to headers and cookies, although for cookies only one value can be defined per cookie.

A more complex example involving multiple configurations which define states based on the requests performed:

request:
  method: GET
  path: "/user/:name"
response:
  statusCode: 200
  headers:
    Content-Type:
    - application/json
  body: >
    {
      "name": "{{request.path.name}}",
      "email": "{{fake.EmailAddress}}",
      "address": {
        "street": "{{fake.StreetAddress}}",
        "city": "{{fake.City}}",
        "zipcode": "{{fake.Zip}}"
      }
    }
control:
  scenario:
    name: user
    requiredState:
    - created
request:
  method: POST
  path: "/user/:name"
response:
  statusCode: 202
control:
  scenario:
    name: user
    requiredState:
    - not_started
    - deleted
    newState: created
request:
  method: DELETE
  path: "/user/:name"
response:
  statusCode: 200
control:
  scenario:
    name: user
    requiredState:
    - created
    newState: deleted

These three configurations emulate the creation, reading and deletion of a user.

The first configuration handles requesting data on a user by performing a GET request to /scenario/<user_name> (notice the :name variable in the path option). It returns mock user information as json if the scenario is in the created state, otherwise returns a 404 http error code.

Next, by performing a POST request to the same endpoint the state of the scenario will be set to created, which will enable us the perform a GET request and return the user’s data.

Finally, by performing a DELETE request to the same endpoint the state will be set to deleted, which will, once again, cause the GET request to return a 404 http error code.

Variables and Fake Data

As you were able to see in the examples above, MMock defines some variables you can use, enclosed in curly braces, like so: {{variable}}.

Most of the request information can be accessed through variables in the response, such as the path, query parameters, cookies, body (respectively: request.path, request.query.<parameter>, request.cookie.<cookie>, request.body), and more.

You can also return the content of files or urls in your response with the variables file.contents(<PATH_TO_FILE>) or http.contents(<URL>).

Lastly, MMock defines some variables that return mock data, such as brands, colors, countries, days, names, ips, phones (respectively, fake.Brand, fake.Color, fake.Country, fake.Day, fake.FullName, fake.IPv4, fake.Phone), and much more!

Conclusion

MMock can be an easy way to define simple mock APIs. The configuration options allows a wide range of behaviors which target the more usual API testing scenarios.

The project is under active development, so expect changes and fixes once in a while!

LEAVE A REPLY

Please enter your comment!
Please enter your name here