Sign in
Log inSign up

How I simplified and optimised Scripting in JSON

Priyanka Navgire's photo
Priyanka Navgire
·Mar 20, 2018

0_xk5OwFMqaHoGRIOK.png

When writing computer programs of even moderate complexity, it’s commonly accepted that “structuring” the program into reusable functions is better than copying-and-pasting duplicate bits of code everywhere they are used. Likewise in JSON Schema, for anything but the most trivial schema, it’s really useful to structure the schema into parts that can be reused in a number of places.

In this article:

  1. Defining and using schema snippets within the script.

  2. Writing scripts for JSON with variable key.

  3. JSON Schema Validation

  4. Reusing the JavaScript code

  5. Run test suite from command line: Newman

  6. Dynamically controlling the test workflows

#1: Defining and using Schema Snippets within the Script.

Let’s consider the following example to proceed with.

    "next_transitions": {
        "31": {"button_text": "Triage"},
        "32": {"button_text": "on-hold"}
    }
}

1.1

Schema of the above example is shown below. As you can see the structure of sub-object(a.k.a. transitionObj) in next_transitions is same — so we don’t want to duplicate that part of the schema everywhere we want the transitionObj. Not only does it make the schema more verbose, but it makes updating it in the future more difficult.

    "$id": "example.com/example.json",
    "type": "object",
    "definitions": {},
    "$schema": "json-schema.org/draft-06/schema#",
    "additionalProperties": false,
    "properties": {
        "next_transitions": {
            "$id": "/properties/next_transitions"
            "type": "object",
            "properties": {
                "31": {
                    "$id": "/properties/next_transitions/properties/31",
                    "type": "object",
                    "properties": {
                        "button_text": {
                            "$id": "/properties/next_transitions/properties/31/properties/button_test",
                            "type": "string"
                        }
                    }
                },
                "32": {
                    "$id": "/properties/next_transitions/properties/32",
                    "type": "object",
                    "properties": {
                        "button_text": {
                            "$id": "/properties/next_transitions/properties/32/properties/button_test",
                            "type": "string"
                        }
                    }

                }
            }
        }
    }
}

Usual schema

Instead of writing schema for same structure again and again, we can write the Schema Snippet for the transitionObj in the Definitions section of the Schema. We can then refer to this Schema Snippet from elsewhere using the $ref keyword. The easiest way to describe $ref is that it gets logically replaced with the thing that it points to. So, to refer to the above, we would include :

                         {"$ref":"#/definitions/transitionObj"} 

The value of $ref is a string in a format called JSON pointer. Now let’s put this together and we get following schema:

    "$id": "example.com/example.json",
    "type": "object",
    "definitions": {
        "transitionObj": {
            "$id": "example.com/example.json",
            "type": "object",
            "$schema": "json-schema.org/draft-06/schema#",
            "properties": {
                "button_text": {
                    "$id": "properties/button_text",
                    "type": "string"
                }
            }
        }
    },
    "$schema": "json-schema.org/draft-06/schema#",
    "properties": {
        "next_transitions": {
            "$id": "/properties/next_transitions",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "31": {"$ref": "#/definitions/transitionObj"},
                "32": {"$ref": "#/definitions/transitionObj"}
            }
        }
    }
}

Defining a snippet and referring it in the schema

###2: Writing scripts for JSON with variable key. In the same example as you can see the key is variable of the transitionObj. The next_transitions object may have more such transitionObj with varying key. Schema for such cases can be written using pattern-properties. Where in you specify the regular expression for variable keys under pattern-properties section as shown bellow:

    "type": "object",
    "definitions": {
        "transitionObj": {
            "$id": "example.com/example.json",
            "type": "object",
            "$schema": "json-schema.org/draft-06/schema#",
            "patternProperties": {
              "^[0-9]*$": {"type": "object"}
            },
            "properties": {
                "button_text": {
                    "$id": "/properties/button_text",
                    "type": "string"
                }
            }
        }
    },
    "$schema": "json-schema.org/draft-06/schema#",
    "properties": {
        "next_transitions": {
            "$id": "/properties/next_transitions",
            "type": "object",
            "additionalProperties": false,
            "allOf": [{"$ref": "#/definitions/transitionObj"}]
        }
    }
}

Optimised way of writing complex schema

You can see the difference in length of the Schema. The above schema will handle any number of transitionObj with variable key(of type integer) without requiring any further changes to the Schema.

###3: JSON Schema Validation: Many modern APIs use some form of JSON Schema to define the structure of their requests and responses. Postman includes the tv4 library, which makes it easy to write tests to verify that your API responses comply with your JSON Schema definitions.

const userSchema = {
    "required": ["userId", "token"],
    "properties": {
        "userId": {"type": "integer"},
        "token": {"type": "string"}
    }
 };

//Test whether the response matches the schema
var user = JSON.parse(responseBody);
tests["Success"] = tv4.validate(user, userSchema);

Define the schema

Of course, you probably wouldn’t want to hard code your JSON Schema in your test script, especially since you may need to use the same schema for many requests in your collection. So, instead, you could store the schema as a JSON string in a Postman Environment variable. Then you can simply use the variable in your test script, like this:

// Load the JSON Schema
const userSchema = JSON.parse(environment.userSchema);

// Test whether the response matches the schema
var user = JSON.parse(responseBody);
tests["Success"] = tv4.validate(user, userSchema)

###4: Reusing JavaScript Code In previous tip it was demonstrated to how easily reuse the same JSON Schema for multiple requests in your collection by storing it in an environment variable. You can also reuse JavaScript code the same way by leveraging the eval() function.

Most APIs have some common rules that apply to most (or all) endpoints. Certain HTTP headers should always be set, or the response body should always be in a certain format, or the response time must always be within an acceptable limit, or like most of the API error response would be same. Rather than re-writing these tests for every request, you can write them once in the very first request of your collection and reuse them in every request after that.

// Save common tests in a global variable
postman.setGlobalVariable("userRegistration", () => {
              var response = JSON.parse(responseBody);
              if(responseCode.code ===200)
                   //Load the JSON Schema
                   const userSchema = JSON.parse(environment.userSchema);
                  //validate
                  tests["Success"]= tv4.validate(response, userSchema);
                  tests["Status code is 200"] = responseCode.code === 200;
                  postman.setEnvironmentVariable("token", jsonData.data.sessiontoken);
                     postman.setEnvironmentVariable("userId", jsonData.data.userId);
             } else if(responseCode.code ===400){
                         //Load the Error JSON Schema 
                        const errorSchema = JSON.parse(environment.errorSchema);
                        //validate
                        tests["Error"] = tv4.validate(response, errorSchema);
                        tests["Status code is 400"] = responseCode.code === 400;
              }
});

In my case the response for both Signup and Signin was same so I reused the code for Signin by calling the above function using eval() as shown below:

 //First, run the common tests
 eval(globals.userRegistration)();

// Then run any request-specific tests
tests["Response time is less than 300ms"] = responseTime <300;

###5: Run test suite from command line: Newman The Postman collection runner is a great way to run all of your tests and see the results, but it still requires you to manually initiate the run. If you want to run your Postman tests as part of your Continuous Integration or Continuous Delivery pipeline, then you’ll need to use the Newman CLI.

newman run <collection.json> -e <environment.json>

###6: Dynamically controlling the test workflows By default, the Postman collection runner, Newman, and Postman Monitors will run each request in your collection in order. But you can use the postman.setNextRequest() function to change the order. This allows you to conditionally skip certain requests, repeat requests, terminate the collection early.