API testing challenges: valid negative scenarios

Enes Kuhn
7 min readDec 4, 2023

API testing is an important aspect of software testing, it is fast, fun, and automatable. Amidst the complexity of modern applications, Swagger documentation steps up as a powerful ally, particularly in handling the nuances of negative scenarios. In this blog post, we’ll dive into how knowing “Swagger documentation syntax” can save us test planning, design, and execution time when it comes to testing an API’s resilience against unexpected and adverse conditions.

OpenAPI Specification

Swagger and the OpenAPI Specification (OAS) are closely related, with Swagger serving as the precursor to the more evolved and standardized OpenAPI Specification. The relationship between Swagger and OAS is a testament to the collaborative effort within the software development community to establish a common standard for describing RESTful APIs.

Swagger, in its earlier versions, introduced a set of tools and a specification for designing, building, and documenting RESTful APIs. However, as the need for a standardized and universally accepted API description format became apparent, Swagger underwent a transformation. This evolution led to the birth of the OpenAPI Specification, which is now the industry-standard for describing RESTful APIs.

OpenAPI Specification (OAS) is an API description format for RESTful APIs. It is a standard that evolved from the Swagger Specification, and the OpenAPI Initiative, established in 2015, manages and advances this standard. The goal of the OpenAPI Initiative is to standardize how APIs are described, making it easier for both humans and computers to understand the capabilities of a service without access to source code or additional documentation.

Swagger was originally an open-source framework for designing, building, documenting, and consuming RESTful APIs. It included a set of tools and specifications that allowed developers to define API structures in a machine-readable format. The Swagger tools provided features such as Swagger Editor for creating API definitions, Swagger UI for interactive API documentation, and Swagger Codegen for generating client libraries and server stubs in various programming languages.

OAS vs Swagger

The OpenAPI Specification builds upon the foundation laid by Swagger, specifically version 2.0, and extends it to create a more robust community-driven standard. While Swagger was initially associated with versions 2.0 and earlier, the OpenAPI Specification encompasses versions 3.0 and beyond.

The relationship between Swagger and the OpenAPI Specification can be likened to a progression from an initial innovative solution (Swagger) to a more formalized and standardized approach (OpenAPI Specification). Swagger was foundational in shaping the expectations and practices for API design, documentation, and testing. As the industry matured, the OpenAPI Specification emerged as a standard, incorporating improvements, clarifications, and additional features.

In essence, Swagger and the OpenAPI Specification are two stages of a continuous journey towards establishing a universal language for describing APIs. While the term “Swagger” is still colloquially used to refer to API specifications, the OpenAPI Specification is the formalized and evolved version, representing the ongoing commitment of the development community to a shared, standardized approach for API description.

YAML and the “required” keyword

In the context of the Swagger documentation for the Petstore API, the publicly exposed documentation that we’ll use u this blog, YAML is used to define and document the API specifications in a human-readable and machine-readable format. YAML (YAML Ain’t Markup Language) is chosen for its simplicity, making it easy for developers to write and understand while also allowing tools like Swagger UI to interpret and present the API documentation in an interactive manner.

Let’s explore an example YAML snippet (find reference here) for the Petstore API:

swagger: "2.0"
info:
description: "This is a sample server Petstore server. You can find out more about\
\ Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).\
\ For this sample, you can use the api key `special-key` to test the authorization\
\ filters."
version: "1.0.6"
title: "Swagger Petstore"
termsOfService: "http://swagger.io/terms/"
contact:
email: "apiteam@swagger.io"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "petstore.swagger.io"
paths:
/pet:
post:
tags:
- "pet"
summary: "Add a new pet to the store"
description: ""
operationId: "addPet"
consumes:
- "application/json"
- "application/xml"
produces:
- "application/json"
- "application/xml"
parameters:
- in: "body"
name: "body"
description: "Pet object that needs to be added to the store"
required: true
schema:
$ref: "#/definitions/Pet"
responses:
405:
description: "Invalid input"
security:
- petstore_auth:
- "write:pets"
- "read:pets"
put:
tags:
- "pet"
summary: "Update an existing pet"
description: ""
operationId: "updatePet"
consumes:
- "application/json"
- "application/xml"
produces:
- "application/json"
- "application/xml"
parameters:
- in: "body"
name: "body"
description: "Pet object that needs to be added to the store"
required: true
schema:
$ref: "#/definitions/Pet"
responses:
400:
description: "Invalid ID supplied"
404:
description: "Pet not found"
405:
description: "Validation exception"
security:
- petstore_auth:
- "write:pets"
- "read:pets"
Pet:
type: "object"
required:
- "name"
- "photoUrls"
properties:
id:
type: "integer"
format: "int64"
category:
$ref: "#/definitions/Category"
name:
type: "string"
example: "doggie"
photoUrls:
type: "array"
xml:
wrapped: true
items:
type: "string"
xml:
name: "photoUrl"
tags:
type: "array"
xml:
wrapped: true
items:
xml:
name: "tag"
$ref: "#/definitions/Tag"
status:
type: "string"
description: "pet status in the store"
enum:
- "available"
- "pending"
- "sold"
xml:
name: "Pet"

In this snippet:

1. swagger: “2.0”: Tells that Swagger document follows version 2.0 of the Swagger specification.

2. info: Contains metadata about the API, such as its description, version, title, terms of service, contact information, and license details.

3. paths: Describes the available API endpoints and their operations. In this case, we trimmed the YAML and showing only /pet.

4. definitions: Defines object types used in the API. In this case, it includes the Pet object with properties such as id, category, name, photoUrls, tags, and status. The xml field specifies XML-related information for this object.

This YAML code essentially outlines the structure and behavior of the Petstore API, including details about endpoints, operations, data models, and metadata. However, you must admit that this looks familiar. Like we already saw this but in different format?!

How does YAML relate to swagger UI?

YAML and Swagger UI are intricately connected in the field of API documentation. In other words, YAML serves as a building block for Swagger UI.

Let’s look at POST /pet example shown in the screenshot above with focus on the parts that are highlighted as green. It clearly says that pet name and photoUrls are required fields (marked with an asterisk “*” next to the field name). In the other hand, name is type of string while photoUrls is string array.

Required, or not?!

In terms of negative testing, the required fields are something that needs to be checked right away. Even though it sounds easy, like we will just send null, it usually is not. Apart from null values, for string types, we can also send “”, “ ” or even remove the property and send JSON body request without it.

The “required” keyword in OAS objects is taken from JSON Schema and tells us:

The value of this keyword MUST be an array. Elements of this array, if any, MUST be strings, and MUST be unique.

An object instance is valid against this keyword if every item in the array is the name of a property in the instance.

Omitting this keyword has the same behavior as an empty array.

The required means property must be present with any value (that eliminates our approach to remove the property from the JSON), AND the value should validate against the value type. In our case, for the pet’s name property:

“name”: “” is VALID (against “required” AND against string type)

“name”: “ “ is VALID (against “required” AND against string type)

“name”: null is NOT VALID (validates against required but does not validate against string type)

{ //json w/o name property } is NOT VALID against required

Note: Note that “null” as a type is not supported in OpenAPI 2.0 but is supported in OpenAPI 3.1, and 3.0 has nullable to handle nulls. So, {“name”: null} is valid against this OpenAPI 3.x schema.

Key takeaways

Moving forward, try to avoid negative scenarios that are not valid! In the first place, if certain property is marked as required, it should be there while testing and we should play around with values with focus on field types and avoid scenarios such as:

Integer: "age": "twelve"
Boolean: "isSubscribed":"true"
Array: "hobbies":"reading"
Enum (example: "active", "inactive") "status":"unknown"
etc...

Also, do not forget that documentation provides us a path, but not strict directions. The logic behind application under test can proof us wrong. Handling negative scenarios on the backend involves implementing validation and error-handling mechanisms such as input validation, error responses, exception handling, and security considerations. However, there are several libraries and frameworks available in different programming languages that can help DEV team with handling negative scenarios, including input validation and error handling, such as FluentValidation (C#), SpringValidation (Java), Express Validator (NodeJS) and others. Knowing that, we can do a little research around our project, do a white box testing, and revise our API testing strategy, for example, totally ignoring invalid input or, in opposite, attacking the backend with more invalid input scenarios.

Conclusion

In wrapping up, the adventure into handling required fields in API development teaches us that even small details like OAS version field can lead to a web of important considerations. What starts as a basic concept becomes a careful dance of checking and securing our systems against unexpected data.

Exploring scenarios where things can go wrong highlights the significance of building a strong backend. It’s not just about expecting the usual; it’s about making our systems strong enough to handle the unexpected. From checking data types to managing errors gracefully, every detail matters to ensure our applications work reliably and securely.

For those eager to delve deeper into the world of OpenAPI and Swagger, I recommend checking out the awesome guide at How to Use OpenAPI and Swagger Spec for Documentation. This resource provides detailed insights and practical tips to enhance your understanding and proficiency in tools for API documentation.

Happy testing!

--

--