How to Execute API Testing with NeoLoad

The days of monolithic applications are coming to a close. Today, more logic is getting distributed throughout the Internet. Mobile devices are becoming more powerful every day, particularly with the onset of 5G network capabilities. The Internet of Things continues to embed itself into the worldwide digital ecosystem. Edge computing puts more computing power on the client side, thus allowing distributed devices to contain intelligence that goes far beyond just presenting data in a UI. Smart devices – phones, cameras, watches, home appliances – are making decisions traditionally found in application logic that resides in a server-side computer.

As client devices become more powerful, they can decide which server-side services they want to use. For example, it’s entirely feasible for a client device to log in to an application domain using any one of some authentication services – Facebook, Google or LinkedIn, for example. Typically such services are represented by APIs.

The real world ramifications are significant, particularly around testing. Modern load testing in a distributed environment is no longer about firing up a bunch of virtual users to exercise web pages. The game has changed. More is required. Load testing in the world of modern, highly distributed applications is all about APIs. Hence, load testing APIs involves a way of thinking that is different from the reasoning that goes into exercising a web page. It’s a different way of doing business.

Allow me to elaborate.

Load Testing APIs: Goodbye HTML, Hello REST

The way most APIs expose themselves for use by consumers is via REST. REST (Representational State Transfer) is an architecture that uses the essential request/response features of HTTP to send data to systems for processing and transformation. REST is about working with data at a lower level of exchange that does not require the graphical intermediation that a web browser provides.

In traditional web page applications, work is done by inputting data into HTML elements such as text boxes, checkboxes, options, and dropdowns. Then, once data is entered, a user typically clicks a button to send the data onto a URL as an HTTP request. The receiving URL gets the data, processes it and then returns a response, typically in HTML when rendering a full web page.

Figure 1 below shows a simple login page that illustrates the request/response process using web pages in a browser.

Figure 1: Load testing a login page involves manipulating input elements in the web page and capturing the response that is returned embedded in HTML

As mentioned above, the web browser serves as an intermediary between the user and the HTTP request, translating the data in a web page into the HTTP request that is sent onto the server. Users need to know nothing about the subtler points of the HTTP request. The web browser does it all. And, when it comes time to test web page applications, the usual practice is to use automated test scripts that emulate the human interaction of entering data into the web page’s HTML elements.

REST, on the other hand, operates at a lower level, directly with the HTTP request. Using REST requires a lot of knowledge about the structure of an HTTP request, mainly learning about HTTP methods as well as working with information in the header of an HTTP request.

Instead of inputting data into a web page, a developer working with REST creates an HTTP request directly. The HTTP request contains the information that would typically be passed by the browser when using a web page. The HTTP request gets moved to a URL that represents a service endpoint of the API. The endpoint URL accepts the request and responds accordingly.

Figure 2 below shows the REST version of simple login page described above in Figure 1. The HTTP request is created and executed using the tool, Postman.

Figure 2: Automated API load testing involves sending requests and recording responses at the HTTP level

Notice at callout (1) in Figure 2 that login data is sent to the URL/login along with data associated with the query parameters for uname and password.

Is putting a password in a query string as plain text a good thing to do?

No, not at all. Never! The login example of putting a password in a query string as plain text as used in this blog is a good example of poor security practice. You should never use this technique for passing sensitive security data. This login example is for illustrating other concepts having nothing to do with passing sensitive data between client and server.

Unlike an interaction among web pages, in which one page might call the URL of another page of HTML that contains embedded server-side script – login.php or login.aspx, for example – REST requires nothing more than a unique path to the endpoint, in the case localhost:3000/login. Typically web servers supporting REST contain a logical code component called a router. The job of a router is to forward the request to a dedicated body of code. The router typically transmits the request according to two pieces of information: the URL path to which the request was sent and the HTTP method associated with the request. Figure 3 below illustrates the mechanics of routing.

Figure 3: A logical router  in the web server passes information in an HTTP request onto code for processing

In this case, the actual server-side code that will process requests forwarded from/login is shown below in Listing 1.

app.get(“/login”, function(req, res) {

//See if the request is from Postman. If not, the value will be null.

   const postmanToken = req.header(‘postman-token’);

//Create a response, no matter the username and password received

   //If the header property, postman-token, has been sent in the request header,

   //return the request headers

   if(postmanToken){

res.send({url: req.originalUrl, headers: req.headers});

}else{

const message = ‘Congratulations! You are logged in.’;

res.send(message);

}

});

Listing 1: Server-side code that creates a response based on custom values in the HTTP Request

The HTTP request passed by Postman to the URL,  localhost:3000/login returns a JSON structure that contains properties that describe the URL of the destination endpoint to which the request was sent and the headers in the request. This particular response is according to the code in Listing 1.

Taking Advantage of the Flexibility of REST

Notice that at Figure 2, callout (2) a custom header, postman-token, has been added to the HTTP request. Postman added this header to the HTTP request as part of its general operation. Each time Postman makes a request; it passes along a unique token in the HTTP headers. It’s the way Postman does business. The server-side code shown in Listing 1 above, looks for the existence of the header, postman-token and will adjust its behavior accordingly. In this case, when the header is present, the server-side login code will return the JSON structure containing URL and headers. When postman-token is not present, all that is answered is a simple message, Congratulations you are logged in. (Please be advised that the code’s failure to perform an authentication lookup on the values, uname, and password is planned. In the real world, such a search would happen.)

One of the key features of REST is the way that header information can be utilized. Under REST, a developer creating a request can add custom headers to the request. These custom header can provide information that is useful and necessary to services supporting REST. For example, it’s typical for a service provider to require that each consumer of the service have a unique token that identifies the consumer’s account to the system. Requiring an account token provides a way to keep track of how many times a customer/consumer uses the service. Many companies price usage of the service according to the number of time a request is made on the service. Thus, the account token not only authenticates the account to the system but also provides useful information for subsequent billing.

Some REST services support the header, x-correlation-id, which is a unique token that identifies the request to the service as one call of many in a transaction. The critical thing to remember is that REST allows you to easily use headers to pass information – conventional and custom – directly in a request. Working with headers in a web page can be done under the covers in client-side code, but allowing users direct access to the headers is typically not possible.

The main takeaway is that REST provides a way to work directly HTTP requests in a very detailed way. Once you understand the details of REST and HTTP requests, creating load tests for APIs become a matter of emulating HTTP requests that are to be emitted in load scenarios. NeoLoad makes such testing a straightforward undertaking.

Using NeoLoad to Test an API

As we’ve covered earlier, most times working with an API requires sending an HTTP request that has been configured according to the task at hand – authenticating a user, for example. As you saw earlier, the way we interacted with the login endpoint using Postman, was to send the request the host, localhost:3000, using the path and the header settings described in Listing 2 below.

GET /login?uname=reselbob&psw=password HTTP/1.1

Host: localhost:3000

Cache-Control: no-cache

Postman-Token: c3b929ea-0989-4164-a21b-7af299e96341

Listing 2: The raw URL and header information sent by Postman to an API endpoint

Load testing the login API endpoint under NeoLoad involves nothing more than emulating this request information using the NeoLoad UI. Figure 4 below shows the details of the NeoLoad UI features used to create the HTTP request used in load testing the login API endpoint.

Figure 4: Under NeoLoad, Virtual Users load test APIs by executing preconfigured HTTP requests

Figure 4, callout (1) shows a configuration of an HTTP request attached to an action for a user, simple_user. Figure 4, callout (2) shows how to define custom request headers using the NeoLoad Advanced dialog. Both the path and header settings use variables defined in NeoLoad to assign values at runtime. A tester will configure the load test under NeoLoad by declaring the number of virtual users to run at test time (based on one or many predefined user paths), as well as the number of requests each virtual user is to generate. A load is created by the test invoking the virtual users.

NeoLoad makes load testing APIs simple in that it treats an API endpoint as a typical HTTP Request. Also, being able to customize request paths and headers using variable data provides an added dimension of load test capability that is useful in more advanced scenarios.

Putting it All Together

APIs are an important part of the world of the modern Internet. REST is fast becoming the dialect of this world. Whereas web browsers hide most of the details of an HTTP request and response interaction, working with REST requires detailed knowledge of the HTTP protocol, particularly concerning HTTP methods and headers. The benefit of working at this level of detail is that it provides developers with a great deal of power and flexibility when designing, using and subsequently testing APIs.

NeoLoad provides the capability to load test APIs easily. Essentially an API is a service endpoint defined by a URL. Thus, you can use NeoLoad to create an HTTP request as a logical user action. Then you design a load test that spins up virtual users accordingly. A load is generated as each virtual user sends the HTTP request defined by the associated action. The test is run, results are gathered, an analysis is made. The process is straightforward.

The biggest adjustment that testers will need to make is the transition from designing and running load tests on web pages to creating HTTP requests targeted at API endpoints. It’s a shift in thinking, moving from the tangible test design that accompanies web page testing to the more abstract approach required for testing APIs. New skills will need to be acquired. But, the investment of time and energy will result in significant returns as APIs continue to grow on the technical landscape.

Learn More

Discover more load testing and performance testing content on the Neotys Resources pages, or download the latest version of NeoLoad and start testing today.

 

Bob Reselman 
Bob Reselman is a nationally-known software developer, system architect, test engineer, technical writer/journalist and industry analyst. He has held positions as Principal Consultant with the transnational consulting firm, Capgemini and Platform Architect (Consumer) for the computer manufacturer, Gateway. Also, he was CTO for the international trade finance exchange, ITFex.
Bob’s authored four computer programming books and has penned dozens of test engineering/software development industry articles. He lives in Los Angeles and can be found on LinkedIn here, or on Twitter at @reselbob. Bob is always interested in talking about testing and software performance and happily responds to emails (tbob@xndev.com).

Leave a Reply

Your email address will not be published. Required fields are marked *