Beliebte Suchanfragen
//

Charge your APIs Volume 31 - Definition-Based API Mocking, Simulation, and Testing with Microcks - Part 1: Introduction

9.10.2024 | 9 minutes of reading time

Key Takeaways

  • API mocking used, e.g., for integration testing, is challenging as it assumes conformance to mocked API functionality, which can incur significant costs as mock complexity increases with API complexity

  • Definition-based API mocking can reduce these costs by automating the derivation and provisioning of mocks from API definitions that are expressed with established specifications such as OpenAPI or AsyncAPI

  • Still, automatically derived mocks may not be fully conformant with mocked API functionality given the limitations in specifications’ expressiveness

  • We present and discuss Microcks, a multi-protocol tool for API mocking, simulation, and testing, that enables automated mock derivation and provisioning from API definitions, but also comes with low-effort features for subsequent mock refinement, e.g., dispatchers for flexible mock routing, as well as support for mock-guided contract testing

  • This article is the first in a three-part series, and focuses on Microcks’ architecture and capabilities in mock derivation and provisioning

Introduction

API mocking is a challenging task. First, mocks are simplified surrogates of other components whose functionality is intentionally limited to a portion of the behavior of these components. Therefore, mocks facilitate testing of this behavior by optimizing for understandability and short provisioning time. However, the tested behavior must be mimicked correctly to ensure test reliability. Second, mock complexity is proportional to API complexity because mock implementations’ susceptibility to errors increases with the number of API endpoints, operations, and responses. Third, mock implementation can become expensive. This is especially true in the context of APIs which typically require not only structural and behavioral mocking but also additional infrastructure for mock provisioning and serving. As a result, efforts related to API evolution and maintenance expand to mocks, and often incur adaptation both of mock code and mock server code.

Since the benefits of mocks in software testing still outweigh their drawbacks, dedicated means are required to effectively cope with the aforementioned challenges. Enter Microcks. Microcks is a multi-protocol tool for API mocking, simulation, and testing. It addresses the above challenges by deriving and serving mocks from API definitions that are written in, e.g., OpenAPI or AsyncAPI, or that are wrapped into Postman collections or SoapUI projects. Hence, for certain use cases, Microcks provides a no-code approach to mock provisioning, but on the other hand comes with sophisticated features that allow for manual mock refinement beyond API definitions. Microcks' target audience ranges from developers, who want to implement lightweight integration and contract tests, over API owners and quality assurers, who want to simulate APIs to assess their conformance, to platform engineers, who are responsible for test environment provisioning in developer portals.

This first article in a three-part series about definition-based API mocking, simulation, and testing with Microcks digs into the tool’s architecture and execution modes. In addition, the article introduces an example API, which will be used throughout the series to practically illustrate a selection of Microcks’ core features, and shows how to leverage Microcks to derive and serve mocks from the API’s definition.

Microcks in (More) Depth

Microcks is an application primarily written in Java and TypeScript. Its logical architecture consists of the following components:

  • broker: Synchronizes data between Microcks’ components by means of Apache Kafka.
  • database: Persists data that is managed by Microcks, e.g., APIs and tests, in a MongoDB database.
  • keycloak: Implements means for authentication and integration with external identity providers.
  • microcks-async-minion: Allows testing of asynchronous API operations that were specified in the webapp component.
  • microcks-postman-runtime: Executes API tests from Postman Collections and stores test results.
  • webapp: Gathers Microcks' UI resources and API endpoints.

A visualization of this architecture is available from Microcks' documentation.

Microcks can run as a permanent server, e.g., based on a dedicated Docker image, which allows for its deployment as a centralized API mocking platform. A development team may then use this platform to (i) maintain mocks of its API definitions; (ii) track mock evolution with Microcks' integrated versioning tracking; and (iii) provide mocks to other teams, thereby enabling all flavors of contract testing. Alternatively, Microcks can be executed as an ephemeral container for integration testing on local developer hardware or as part of remote CI/CD pipelines. For this purpose, Microcks' Testcontainers module comes into play.

Independent of its execution mode, Microcks pursues three goals:

  • Shorten feedback loops when creating, adapting, and evolving APIs and their definitions.
  • Speed up development processes by ad-hoc mock derivation from API definitions. This approach enables, among others, the resolution of team dependencies in which one team has to wait for the implementation of an API by another team before being able to integrate with this API.
  • Recognize discrepancies in the conformance of API implementations with their definitions each time when changes to one of these artifacts occur.

The Example API: Customer Management

Disclaimer: We discovered the example API, which we are relying on to practically illustrate a selection of Microcks’ core features during the article series, in the wild, and found it quite coherent, understandable, and therefore suitable to exemplify and discuss a selection of Microcks' core features. However, this API also bears some potential for optimization and does not reflect a style of API design that we would advise.

The example API represents functionality for customer management. More precisely, it specifies (i) the retrieval of customer kinds which the provider, i.e., the provider, offers for customer categorization; (ii) a credential-based login mechanism for access token retrieval; and (iii) the obtainment of details about managed customers.

The example API is synchronous, HTTP-based, and comprises the following operations for the aforementioned use cases:

  • GET /customer_kinds: This operation allows the retrieval of information about the kinds of customers the provider supports for customer categorization. In order to invoke the operation, the caller must provide a fixed, predefined API key in the form of a JSON Web Token (JWT) in the HTTP header field api_key. The following JSON snippet shows an example response of the operation to a valid request. Structurally, the response links the ID of a customer kind with its English description:
    {
      "1": "Private person",
      "2": "Commercial enterprise",
      "3": "Public institution"
    }
  • GET /customer_kinds/{id}: This operation delivers further information about a certain customer kind. For its valid invocation, the operation expects the same API key as GET /customer_kinds. Furthermore, the customer kind, which is identified by the path parameter id, must correspond to one of the customer kind IDs in the response to a valid request against GET /customer_kinds. For instance, a call of GET /customer_kinds/3 could yield a response similar to this one:
    {
      "id": "3",
      "kind": "Public institution",
      "createdOn": "12.07.2024 12:32:59",
      "updatedOn": ""
    }
  • POST /login: This operation enables customers to log into the provider, e.g., to review their name or address information (see below). POST /login expects the same API key as GET /customer_kinds and GET /customer_kinds/{id}. Additionally, requests against the operation must comprise a JSON object that specifies values for the two fields email (as an equivalent to the customer’s username in the provider) and password (to complete the customer’s credentials). Note that the example API leverages this kind of basic authentication only for the purpose of demonstrating some Microcks features in a self-contained fashion. Due to its insecure nature, basic authentication should not be applied in real-world software projects as there exist better alternatives like token-based authentication. In case of valid requests that include the correct API key and matching credentials, the operation emits JSON responses similar to the following:
    {
      "customer_token": "eyJhbGciOiJIUzI1NiJ9...."
    }
    The value of the field customer_token represents a JWT of a customer who successfully logged into the provider.
  • GET /customer: With a valid customer_token issued by the previous operation, this operation enables users to retrieve certain information about their customer record in the provider. For this purpose, the customer_token must be transmitted via the HTTP header field customer_token. A valid request against GET /customer may then result in a response similar to the following:
    {
      "email": "some_customer@example.org",
      "first_name": "Casey",
      "last_name": "Rice",
      "company_name": "",
      "address": "6377 Fallon Pine, North Emoryburgh, Anguilla"
    }

A typical interaction scenario with the above operations of the example API is as follows:

  1. Invocation of GET /customer_kinds with the fixed api_key to retrieve all supported customer kinds.
  2. For each customer kind id returned by the previous call, invocation of GET /customer_kinds/{id} with the same api_key as before, e.g., to display the corresponding kind’s description on the UI of a consuming application.

Another interaction scenario comprises the following steps:

  1. Invocation of  POST /login with the fixed api_key and a customer’s credentials in order to obtain their customer_token.
  2. Invocation of GET /customer with this token to obtain further customer information.

The OpenAPI definition of the example API is contained in the file “customers.yaml” in the Git repository dedicated to the article series. The repository also gathers two Java applications. The first one  is a complete demo implementation of the example API and enables experimentation with Microcks’ capabilities in contract testing. The second one shows how to use Microcks for integration testing leveraging its Testcontainers module. The following section shows how to register the example API in Microcks to derive and provision mocks. The followup articles then draw on the API and its accompanying Java applications to describe and illustrate the practical usage of further Microcks core features.

Getting APIs Into Microcks

Microcks offers several opportunities for API registration to make subsequent mock provisioning feasible:

  • Direct APIs: This functionality allows for the manual creation of definitions for synchronous or asynchronous APIs. To this end, Microcks' web UI integrates an interactive workflow to configure API properties. These properties comprise interaction style (synchronous or asynchronous), and API name, version, resource, and reference payload. After workflow completion, Microcks first converts configured API properties into definitions whose format and structure depend on the selected interaction style. For synchronous APIs, Microcks generates OpenAPI definitions with CRUD operations for resource manipulation. For asynchronous APIs, Microcks generates AsyncAPI definitions with resource subscription operations. The previously mentioned reference payload enables further differentiation of generated operations. From the generated definition, which can be downloaded from Microcks' web UI, Microcks serves mocks for the specified API operations.
  • Importers: Importers enable the upload of existing API definitions from which Microcks derives and serves mocks. Definition imports can also be automated leveraging the Import Jobs feature. It supports continuous mock provisioning, e.g., from periodic scanning of source code repositories. 

The existing definition for the example API introduced above can be uploaded to Microcks with the OpenAPI Importer. That is, from a running Microcks instance select the “Importers” category from the main navigation in Microcks' web UI, click on “Upload”, browse to the definition file, and hit the “Upload” button. After successful upload and navigating to the “APIs | Services” category in Microcks' main navigation, Microcks displays the imported API:

The “Details” button leads to an overview of the imported API operations, for which Microcks already serves mocks after their successful import:

Collapsing an operation displays details about served mocks, including mock URL and response:

Conclusion and Outlook

This article is the starter of a three-part series about definition-based API mocking, simulation, and testing with Microcks - a tool for API mocking that enables automated mock derivation and provisioning from API definitions, and also integrates features for subsequent mock refinement and mock-guided contract testing. While this article focused on Microcks’ architecture, and showed how to derive and serve mocks from API definitions, the next article of the series will cover the details of mock data specification as well as mock refinement with request validations and routing.

share post

Likes

1

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.