API Testing with PactumJS

PactumJS is a free and open-source REST API automation testing tool. It enables engineers to write all types of tests against backend servers (REST APIs and GraphQL). It supports all kinds of API testing needs for microservices. These are mentioned below.

We can write the tests in PactumJS using two styles.

  • Chaining the request & expectations (Builder Style)
  • Breaking the request & expectations (BDD Style)

More you can find on this link.

In this article we well be focussing upon writing the E2E tests for both Rest API and GraphQL. Mostly I will be using Builder Style to write the E2E tests, but choice is yours. You can opt any of the style which suits you the best. There are different testing frameworks provided by PactumJS like Jest, Mocha and Jasmine. I will be using Mocha for demonstration.

Writing the spec in PactumJS is super easy. There are lot of API’s method provided by PactumJS for our rescue.

Entrypoint to start the PactumJS tests is spec(). It represents a single API call or a single test case. withMethod() defines the HTTP method to use in the request. withPath() specifies the request url or path.withQueryParams() is used to set the parameters attached to the end of a url. Then we have several assertions API provided by PactumJS. You can read more about that here.

You can see I have used toss() in the end. It tests and returns the response object. It is optional in most scenarios. One thing you need to keep in mind, assertions like expectStatus() should be called before calling the toss().

Rest API’s

Let’s see some use case, how we can automate different requests (GET, POST, PUT, DELETE) using PactumJS. We will be using https://reqres.in/ to perform API testing.

GET

There are two tests in this spec.

  1. Get Users in a specific page : In this spec we are testing a GET request with Query Parameters. As you can see I am validating the response using expectJsonLike().It performs partial matching of JSON response. To read more about this please follow the link. There are other assertions available, you can use according to your need. You can read more about assertions here.

2. Get Users by ID: In this spec we are getting a user info corresponding to a particular ID. For this request we will be making use of path parameters. withPathParams() will be useful while using path parameter. If you notice carefully I am using handler $F{GetRandomNumber:1,10}. PactumJS provides a lot of handlers. You can read more about them here. I have used a dataFuncHandler. It is used to execute a function. As you can see I have create a dataFunHandler with the name GetRandomNumber which accepts two number and return a random value between these two numbers. In order to call this handler you have to use the syntax “$F{HandlerName:args}”. In our case it would be $F{GetRandomNumber:1,10}

POST

There are 3 tests in this spec. I will be talking about how you can derive the data for POST request in these tests.

  1. Create a new User using Factory Data: This is the most common approach and can be used in many of the available tools. I will be using Rosie and Faker to create my test data.
const Factory = require("rosie").Factory;
const { faker } = require("
@faker-js/faker");
const user = () => {
Factory.define("user")
.attr("name", () => `${faker.name.firstName()} ${faker.name.lastName()}`)
.attr("job", () => faker.company.name());
return Factory.build("user");
};
module.exports = {
user: user,
};

You can see how I have generated the test data using combination of Rosie and Faker. These are really a saviour when it comes to Data Management for our tests.

2. Create a new User using template: PactumJS provides the DataManagement using DataTemplate and DataMaps. To read more about DataTemplate click here. addDataTemplate() Adds a data template which can be used across pactum tests and mock server.

stash.addDataTemplate({
User: {
name: `${faker.name.firstName()} ${faker.name.lastName()}`,
job: faker.company.name(),
},
});

As you can see I am constructing the request body using a DataTemplate. DataTemplate can be called in your test like

'@DATA:TEMPLATE@': 'Template name' in our case it would be '@DATA:TEMPLATE@': 'User'

3. Create a new User using Data Map: Like DataTemplate, PactumJS also provides DataMap. To learn more about this follow the link.

stash.addDataMap({
User: {
name: `${faker.name.firstName()} ${faker.name.lastName()}`,
job: faker.company.name(),
},
});

Synatx is pretty much the same for both DataTemplate and DataMap. Only difference is how we call these in our tests. We call the field from DataMap using the Syntax “$M{DataMapName.property}”. In our example it should be “$M{User.name}” where User is DataMap name and name is property on User DataMap.

You can retrieve the value of DataTemplate using getDataTemplate() method.
To read more on this click here.

GraphQL

Let’s see how we can automate GraphQL API. In this section I will be focussing from automating a simple query(with or without variables) to some complex mutations and fragments. I am sure after this section you will comfortable in automating GraphQL.

We will be making use of available spaceX GraphQL playground(https://api.spacex.land/) to automate our API.

As you can see we have three tests in this spec. Let’s breakdown each test to get the understanding on individual tests.

  1. Get Company info for spaceX: In this test we will be writing a GraphQL query to get the company information. Query will look something like this

I guess , query is straightforward. We are just querying the server to give us certain information related to company. We will be passing our query to the methodwithGraphQLQuery(query).Rest of the validations remains same that we have seen in our previous tests.

2. Validate Company info using assert handlers: The only difference in this test is that I will be using assertHandler to make assertions. We have already seen DataFuncHandler when we were talking about GET Request. Assert Handlers helps us to reuse the custom JavaScript assertion code on a JSON. Let’s see how we can define assertHandler

handler.addAssertHandler(“ceo”, (ctx) => {
// validating ceo and cto field as “Elon Musk”
return ctx.data === “Elon Musk”;
});

Some points about Assert Handler

  • Handler name will be prefixed with # while using in json.
  • Handler function should return a boolean.

.expectJsonLike({
data: {
company: {
ceo: “#ceo”,
coo: “Gwynne Shotwell”,
cto: “#ceo”,
employees: 7000,
},
},
})

While calling the assert handler we need to use “#” followed by name of handler in our case, it would be “#ceo”.

3. Get Dragons info for spaceX using GraphQL variables: Mostly we need to paas some parameters to fetch the details from the server. This can be achieved by using GraphQL variables. Let’s see how we can write query for this.

As we can see query is accepting a variable $limit of type Int. To pass limit to this query we need to utilise the variables. These variables can be passed as a JSON object.

{limit: 2}

To send this variables to GraphQL query, PactumJS exposes a method withGraphQLVariables(variables). You just need to pass variables to this query like withGraphQLVariables({limit: 2}). Now the rest of the code remains the same. Choice is up to you , if you want to extend the assertion feel free to do so.

A GraphQL fragment is a piece of logic that can be shared between multiple queries and mutations.

Let’s see how a fragment in GrapQL looks like

As you can see for creating a Fragment you need to use keyword fragment.

fragment DragonInfo on Dragon {
description
id
}`

I have created a DragonInfo Fragment. Name can be anything. Then you need to define the schema on which you need to create Fragment. In our case it is Dragon. Now you need to define the fields which you want to use across the queries and mutations.

To use a Fragment in queries and mutations, you just need to call it in queries and mutations with syntax …FragmentName in our case it would be …DragonInfo

You can see in the above code how we have used our Fragment in getDragonInfoUsingFragment().

Tests will be similar to what we have written so far for simple queries. Refer to the below screenshot for the code.

In GraphQL, you insert, update or delete data with mutations. A Mutation is a GraphQL Operation that allows you to insert new data or modify the existing data on the server-side. You can think of GraphQL Mutations as the equivalent of POST , PUT , PATCH and DELETE requests in REST.

Let’s see how we can create a mutation in GraphQL.

We need to use the keyword mutation then name of the mutation. As mutation allows to insert or modify the data, we need to provide some data to be acted upon to the mutation query. In our case we need to provide CreateAlbumInput! . Schema for CreateAlbumInput looks like

type CreateAlbumInput {
title: String!
userId: ID!
}

You can see ! mark for the fields. It means that it is required field. You can read more about the schemas in the playground link.

Before jumping to the test, let me show you how mutation will looks like in playground.

Now lets’s see how we can automate this.

Test will looks like other GraphQL tests, but one thing you will notice here

.withGraphQLVariables({
input: {
title: “MLTR”,
userId: “ab163e1d-80ce-4faf-a7af-358163f3635f”,
},
})

I am passing title and userId to the GraphQL variables as these are required fields for the CreateAlbumInput!.

Absence of any of these fields in the query variables will result in the failure.

Docker

I have uploaded the above code in my Github repo. You can clone the repo from the link. To run the test you just need to run two commands

npm ci

npm run test

execution-locally

To run the tests in Docker, I have created a Dockerfile. Please see the Dockerfile here. https://github.com/jeeshan12/pactumjs-automation/blob/main/Dockerfile

To build the image you need to run the command from the terminal

docker build -t <<imageName>> .

e.g.

docker build -t jeeshan/pactumjs-automation .

You will see output something like this.

image-build

To run the tests use the below command :

docker run --rm jeeshan/pactumjs-automation run test —- —-timeout 100000
execution-docker-containers

There is lot more to PactumJS and a lot more you can do with the library.
I guess that’s all for this article.

In case of any help required on this, you can always reach out me on my LinkedIn.

https://www.linkedin.com/in/mohdjeeshan/

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store