Skip to main content

Dictionary

When Specmatic generates requests while running tests or responses while running as a stub and no examples have been provided, Specmatic will generate values for fields based on the schema in the OpenAPI specifications.

While the generated values will conform to the schema, they may not be meaningful from the point of view of your business domain. Also at times you want to have more control on the exact values that are used for certain fields.

This is where dictionary capability helps you define domain-specific values which Specmatic can use in the absence of examples. Specmatic will look up values for fields in the dictionary defined by you while generating requests or responses.

The dictionary can be supplied in either YAML or JSON format. When the dictionary file name follows the convention <spec-file-name>_dictionary.<format>, Specmatic will automatically pick it up in the context of the corresponding API specification file.

Structure

Fields are represented as nested properties that reflect the hierarchical structure of the schema.

  • Each entry in the dictionary maps a schema field to either a single value or a list of values.
  • If a single value is specified, it will be used directly.
  • If a list is specified, one of those values will be selected at random.

Basic Field Mapping

For example, given the Employee schema as follows:

components:
schemas:
Employee:
type: object
properties:
name:
type: string
age:
type: integer

Corresponding dictionary entries for the name and age properties would be:

Employee:
name: John # Single-value
age: # Multi-value
- 20
- 30
- 40
  • The name field is supplied as a single value and will be used directly.
  • The age field is supplied as a list of values, from which either 20, 30, or 40 will be pseudo-random selected.

Parameters Mapping

In the PARAMETERS section, parameters should organized based on their location: PATH, QUERY, or HEADER. Each key within these groups denotes the parameter name, which may correspond to either a singular value or an array of potential values. When multiple values are specified, one will be selected pseudo-randomly during runtime.

PARAMETERS:
PATH:
userId: 123
orderId: 456
QUERY:
sort: date
page: 10
HEADER:
x-request-id: req-12345

Nested Properties

For nested structures, properties are represented as nested keys, where each level corresponds to a that level in the schema hierarchy. For example, given the Employee schema as follows:

components:
schemas:
Employee:
type: object
required:
- name
properties:
name:
type: object
required:
- first_name
properties:
first_name:
type: string
last_name:
type: string

Corresponding dictionary entries for the first_name and last_name property would be:

Employee:
name:
first_name: John
last_name: Smith

Handling Arrays

For properties that are arrays, the values must also be an array in the dictionary For example, given the Employee schema as follows:

components:
schemas:
Employee:
type: object
required:
- aliases
properties:
aliases:
type: array
items:
type: string

Corresponding dictionary entries for the aliases array would be:

Employee:
aliases:
- "John"
- "Jane"

The entire array will be used

Nested properties in Arrays

For example, given the Employee schema as follows:

components:
schemas:
Employee:
type: object
required:
- aliases
properties:
aliases:
type: array
items:
type: object
required:
- first_name
properties:
first_name:

Corresponding dictionary entries for the first_name property within the aliases array would be:

Employee:
aliases:
- first_name: "John"
note

This nesting behaves correctly because first_name is not defined as a top-level property in the schema. It is also not necessary to specify all properties of the object within the array, any missing properties will be populated by Specmatic.

Referencing Other Schemas

When a schema component utilizes $ref to reference another schema component, the dictionary for the referenced schema component with be defined with that schema component as the root entry (in other words directly start with the fields of the referenced schema)
For example, given the following Employee and Address schemas:

components:
schemas:
Employee:
type: object
required:
- addresses
properties:
first_name:
type: string
addresses:
$ref: '#/components/schemas/Address'
Address:
type: object
required:
- city
properties:
street:
type: string
city:
type: string

Corresponding dictionary entries for any property defined in the Address schema would be:

Address:
city: "New York"
street: "Broadway"

Notice that the keys begin with Address instead than Employee, because the dictionary accesses the fields from the referenced schema. This makes for convenient dictionary definition without have to nest the values deeply.

Dictionary Generation Commercial

Manually creating a dictionary can be quite an involved process, especially when the schema is complex. This is where, specmatic/enterprise offers a convenient method to generate dictionaries from OpenAPI specifications and existing examples.

note

Automated dictionary generation is only available in the commercial version of Specmatic. For further details, please check the pricing page.

Let's take a look at how we can generate a dictionary from an OpenAPI specification along with existing examples

Specification

Create an OpenApi Specification file named employees.yaml as follows:

openapi: 3.0.0
info:
title: Employees
version: '1.0'
servers: []
paths:
/employees:
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/EmployeeDetails'
responses:
'200':
description: Employee Created
content:
application/json:
schema:
$ref: '#/components/schemas/Employee'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Employee:
type: object
required:
- id
- name
- department
properties:
id:
type: integer
employeeCode:
type: string
name:
type: string
department:
type: string
EmployeeDetails:
type: object
required:
- name
- department
properties:
name:
type: string
department:
type: string
Error:
type: object
required:
- message
properties:
message:
type: string

Examples

Given that we have an OpenAPI specification, we should add a few examples for the /employees endpoint. Create a folder named employees_examples in the same directory as your employees.yaml file

  1. Let's create an example for john named employee_john.json:

{
"http-request": {
"path": "/employees",
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"name": "John",
"department": "IT"
}
},
"http-response": {
"status": 200,
"body": {
"id": 123,
"employeeCode": "EMP123",
"name": "John",
"department": "IT"
},
"status-text": "OK",
"headers": {
"Content-Type": "application/json"
}
}
}
  1. Similarly, create an example for jane named employee_jane.json:

{
"http-request": {
"path": "/employees",
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"name": "Jane",
"department": "HR"
}
},
"http-response": {
"status": 200,
"body": {
"id": 456,
"employeeCode": "EMP456",
"name": "Jane",
"department": "HR"
},
"status-text": "OK",
"headers": {
"Content-Type": "application/json"
}
}
}
  1. Now let's create a bad-request example named bad_request.json, where we've mutated the department field:

{
"http-request": {
"path": "/employees",
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"name": "James",
"department": 123
}
},
"http-response": {
"status": 400,
"body": {
"message": "BAD REQUEST - Invalid field: department, value: 123 <number>, expected: <string>"
},
"status-text": "Bad Request",
"headers": {
"Content-Type": "application/json"
}
}
}
note

The _examples suffix is a naming convention that indicates to Specmatic to look for examples in that directory.

Generating the Dictionary

important

Create a file named employees_dictionary.yaml in the same directory as your employees.yaml file with an empty object i.e. {}, so we volume mount this file to the Docker container

After setting up, we can execute the examples dictionary command using the specmatic/enterprise Docker image and provide the path to our employees.yaml file as shown below:

docker run --rm -v "$(pwd)/employees.yaml:/usr/src/app/employees.yaml" -v "$(pwd)/employees_examples:/usr/src/app/employees_examples" -v "$(pwd)/employees_dictionary.yaml:/usr/src/app/employees_dictionary.yaml" specmatic/enterprise examples dictionary --spec-file employees.yaml

Understanding the Dictionary

After executing the command, the employees_dictionary.yaml file will be updated with the following contents:

EmployeeDetails:
name:
- Jane
- John
- James
department:
- HR
- IT
Employee:
id:
- 456
- 123
employeeCode:
- EMP456
- EMP123
name:
- Jane
- John
department:
- HR
- IT
Error:
message:
- 'BAD REQUEST - Invalid field: department, value: 123 <number>, expected: <string>'
  • Note how the hierarchy of the dictionary reflects the schema outlined in the OpenAPI specification.
  • Each key in the dictionary aligns with a property in the schema, while the values represent the possible values for that property.
note

We have not included the invalid value of department in the bad-request example. This filtering is not restricted to 4xx examples, the command will include only valid values in the dictionary, allowing it to be executed even with invalid examples.

Example Directory Parameter

If you have examples in some other existing directory, you could provide the path to that directory using the --examples-dir option as shown below:

docker run --rm -v "$(pwd)/employees.yaml:/usr/src/app/employees.yaml" -v "$(pwd)/examples:/usr/src/app/examples" -v "$(pwd)/employees_dictionary.yaml:/usr/src/app/employees_dictionary.yaml" specmatic/enterprise examples dictionary --spec-file employees.yaml --examples-dir ./examples

Dictionary with Contract Testing

The Dictionary can be utilized in contract testing, allowing Specmatic to use the values defined in the dictionary when generating requests for tests. To illustrate this process, we will use the previous specification and dictionary as an example.

For the moment, we will remove the employees_examples directory to observe how contract testing operates without examples.

Run the tests

Now to execute contract tests on the specification using the dictionary a service is required, we will utilize service-virtualization for this purpose.

java -jar specmatic.jar stub employees.yaml

Next, execute the contract tests by running the following command:

java -jar specmatic.jar test employees.yaml

We can now examine the request sent to the service by reviewing the logs.

POST /employees
Accept-Charset: UTF-8
Accept: */*
Content-Type: application/json
{
"name": "James",
"department": "IT"
}
note

The values from the dictionary are used in the requests. Keep in mind that these values are selected in a pseudo-random manner from the list, so you may encounter different values each time.

Generative Tests

As it's evident that only valid values can be included in the dictionary. hence, generative tests will ignore the values in the dictionary for the key being mutated. The other keys will still retrieve values from the dictionary if available; otherwise, random values will be generated.

For instance, if you execute the specification with generative tests enabled, one of the request will appear as follows:

POST /employees
Accept-Charset: UTF-8
Accept: */*
Content-Type: application/json
{
"name": null,
"department": "IT"
}

In this case, the key name is being mutated, which results in the value from the dictionary being disregarded. While the values for department is still being retrieved from the dictionary

Dictionary with Service Virtualization

The Dictionary can be utilized with service virtualization, where Specmatic will leverage the values in the dictionary to generate responses for incoming requests. To illustrate this functionality, let's consider the previous specification and dictionary as an example. We will remove the employees_examples directory to observe how service virtualization functions without examples.

Run Service Virtualization

To start service virtualization, use the following command:

java -jar specmatic.jar stub employees.yaml

Making Requests

Execute the following curl command:

curl -X POST -H "Content-Type: application/json" -d "{\"name\":\"Jamie\",\"department\":\"IT\"}" http://localhost:9000/employees

You'll get the following response:

{
"id": 123,
"employeeCode": "EMP123",
"name": "Jane",
"department": "IT"
}
note

The values from the dictionary are used in the responses. Keep in mind that these values are selected in a pseudo-random manner from the list, so you may encounter different values each time.

Dictionary with Examples

You can also utilize dictionary with the example commands and the examples interactive server, allowing values to be retrieved from the dictionary for both request and response generation of examples, refer to External Examples for more details.