Unleashing the Power of OpenAPI in Test Automation
Logo from The OpenAPI Initiative (OAI)

Unleashing the Power of OpenAPI in Test Automation

Before we can unleash the power of OpenAPI in testing, we first need to understand what OpenAPI is. OpenAPI specification is an open-source format for describing and documenting APIs. The specification was originally created to keep the API design and documentation in sync (see Swagger) and is now a ubiquitous standard for designing and describing RESTful APIs.

The OpenAPI definitions can be written in JSON or YAML. In the example below you can see a YAML representation of OpenAPI 3 for a “pet store”. Note that each path (/pets and /pets/{petId}) has its own properties and can reference to a common components section.


openapi: "3.0.0

info:

  version: 1.0.0

  title: Swagger Petstore

  license:

    name: MIT

servers:

  - url: http://petstore.swagger.io/v1

paths:

  /pets:

    get:

      summary: List all pets

      operationId: listPets

      tags:

        - pets

      parameters:

        - name: limit

          in: query

          description: How many items to return at one time (max 100)

          required: false

          schema:

            type: integer

            format: int32

      responses:

        200:

          description: An paged array of pets

          headers:

            x-next:

              description: A link to the next page of responses

              schema:

                type: string

          content:

            application/json:    

              schema:

                $ref: "#/components/schemas/Pets"

        default:

          description: unexpected error

          content:

            application/json:

              schema:

                $ref: "#/components/schemas/Error"

    post:

      summary: Create a pet

      operationId: createPets

      tags:

        - pets

      responses:

        201:

          description: Null response

        default:

          description: unexpected error

          content:

            application/json:

              schema:

                $ref: "#/components/schemas/Error"

  /pets/{petId}:

    get:

      summary: Info for a specific pet

      operationId: showPetById

      tags:

        - pets

      parameters:

        - name: petId

          in: path

          required: true

          description: The id of the pet to retrieve

          schema:

            type: string

      responses:

        200:

          description: Expected response to a valid request

          content:

            application/json:

              schema:

                $ref: "#/components/schemas/Pets"

        default:

          description: unexpected error

          content:

            application/json:

              schema:

                $ref: "#/components/schemas/Error"

components:

  schemas:

    Pet:

      required:

        - id

        - name

      properties:

        id:

          type: integer

          format: int64

        name:

          type: string

        tag:

          type: string

    Pets:

      type: array

      items:

        $ref: "#/components/schemas/Pet"

    Error:

      required:

        - code

        - message

      properties:

        code:

          type: integer

          format: int32

        message:

          type: string"

So how OpenAPI can help with test automation? In this article I want to spot several aspects that OpenAPI can propel or even be a game changer for your automated testing system.

 

API documentations

The first and most obvious way that OpenAPI can assist you in managing the usage of RESTful API in testing, is by providing API documentations from the OpenAPI definition. This can be done by reading the properties for each API endpoint directly from the OpenAPI YAML/JSON or by using a documentation generator that transforms the OpenAPI into a nice readable HTML document (e.g., Swagger UI). For example, if we need to use the "pet store" API described in the YAML snippet above and we want to get all the pets, we will look for /pets endpoint with method get. Under parameters we can see that there is a single optional limit parameter that is passed as query with type of Integer. We can accordingly use an http client and create a RESTful request that would look like this:


const petList = client.get(‘https://petstore.com/pets’);

Or if we want to limit the returned list to the first 10 pets:


const petList = client.get(‘https://petstore.com/pets?limit=10’);

OpenAPI Generator

While using the OpenAPI documentation as described above may fit small automation projects, it may be less practical for large projects due to two main reasons: first, because large and growing projects would require a tedious work in writing all the requests; we must select a suitable HTTP verb and connect to the correct endpoint and must pass all parameters in the correct format and parse the returned values. The second reason relates to code maintenance due to changes in the API schemes evolving during the system under testing (SUT) lifecycle. Fixing each of the requests in a large automation project would probably be complicated and time consuming; even worse, it would catch your automated tests unprepared, leading to test failures due to legitimate changes in the API schemes (just imagine this occurring in your CI/CD pipeline 😱). One way to solve this problem is to use the OpenAPI Generator. The OpenAPI Generator, as its name implies, generates code from an OpenAPI specification. It can create code for client libraries, server stubs, documentation and configuration and it supports almost all the widely used languages and frameworks. For client developers, OpenAPI Generator might be a reminiscent of the famous Simple Object Access Protocol (SOAP) where client applications are automatically generated from a WSDL contract.

Installing the OpenAPI Generator is out of this article scope, but you can start here to get more information about this issue.

To briefly illustrate how OpenAPI Generator works for you, I will use our "pet store" example. The relevant outcome of running the generator on the "pet store" scheme would be a PetsAPI class. At this time, we would get all the pets using the generated class as follows:

First, we need to instantiate the PetsAPI class:


const petsAPI = new PetsAPI(‘https://localhost’);

then, we call the generated listPets method to get all the pets:


const petList = petsAPI.listPets();

Or if we want to return the first 10 pets:


const petList = petsAPI.listPets({limit:10});

Note that thanks to the OpenAPI Generator we shouldn’t care about the correct endpoint anymore, and when the endpoint is changed, it would be reflected in the generated code. Thus, API request code is now maintenance-free as it syncs with the latest scheme. As a bonus, your IDE would be "aware" of the listPets method signature while you write your code, so creating a request with the exact parameters becomes super easy.

 

Mocking path validation

Modern automation frameworks are capable of mocking and modifying network traffic. Let’s say that "pet store" has a web client, and its main page has a ‘Get pets’ button that is activating behind the scenes a /pets get request. The framework enables us to mock the server response like this:


await page.route('https://localhost/pets', async route => 

  const mockedResponse = {pets: [{name:‘llama’}, {name:’rabbit’}]};

 

  await route.fulfill( mockedResponse );

});{

Now, every time we click the ‘Get pets’ button the browser would get the mocked data (llama, rabbit) instead of getting a response from the server. But what if the /pets path would change sometime in the future to /fluffypets ? The sad answer is that the framework would still attempt to mock the old irrelevant path, while the relevant path /fluffypets would remain un-mocked and we wouldn’t even know it!  This is a severe problem that requires a solution. One way to handle it is by writing additional method that would verify the existence of the path we want to mock before executing the route mocking method. This could be simply done by searching for the mocked path under paths in the OpenAPI scheme (see the "pet store" YAML above). If the path has been changed, it won’t be found, and an exception error is thrown accordingly. The drawback of this simple approach is that your test would fail once the change has been made and you will need to fix the path manually. To make this mocking process maintenance-free altogether it would be better to get the mocked path (e.g., /pets) directly from the OpenAPI scheme based on its operationId property, for example.

To conclude, OpenAPI specification has changed the landscape of modern API design. More and more businesses investing in API design with OpenAPI. Therefore, automation developers should embrace this technology to keep their working environment relevant for this new era. I hope you find this article helpful, and I would love to get your feedback about it.🔳

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics