Writing API Tests with the Composer
The API Testing Composer enables you to quickly generate API functional tests (no coding experience required) and/or code them from scratch. You can reuse these tests as end-to-end integration tests and load (stress) tests. In turn, load tests can be reused as monitors for performance testing.
What You'll Needβ
- A Sauce Labs account (Log in or sign up for a free trial license).
- An existing API Testing Project. For details on how to create one, see API Testing Quickstart.
Creating a Test with the Composerβ
Create a Testβ
In Sauce Labs, click API Testing.
On the Projects page:
If you have no tests or projects yet, in the Write your own test box, click Use Composer.
If you have a project but no tests, on the Projects page, click Write your own test.
If your project has tests, click Create Test and then click From Scratch.
- In the New Test box, enter a test name, test description (optional), and tags (optional), and then click Create Test.

note
You can use either the Visual composer (guides you through building components, with no coding required) or the Code composer (requires you to write code from scratch). For this guide, we're using Visual.
For more information, see Input Sets and Visual View and Code View.
Edit a Testβ
To edit a test at any time, on the Projects page, on the Tests tab, hover over a test name and then click Edit Test.

Add Test Componentsβ
When test components are combined, they act as our test logic. There are three component types available in API Testing:
Add an I/O Request Test Componentβ
To create a simple GET
request and validate that response is correct:
In API Testing, on the Compose page, click the Add component button.
In the list of component options, click the GET component.
In the GET request window, in the Url field, enter https://api.us-west-1.saucelabs.com/rest/v1/public/tunnels/info/versions.
This endpoint will return a JSON response body.
In the Variable field, enter payload. This variable stores the response, so it can now be referred to as payload.
Leave the rest of the fields blank and then click the Confirm changes icon.
The result should look like the following:
For more information, see I/O Request Test Components.
Add an Assertion Componentβ
In API Testing, on the Compose page, click the Add component button.
In the list of component options, click the Assert Exists component.
In the Assert exists window, in the Expression field, enter
payload.downloads
. This expression checks for the downloads field in the json response body.Leave the rest of the fields blank and click the checkmark to confirm the changes.
The result should look like the following:
For more information, see Assertion Test Components.
Additional Exampleβ
In the following example, the expression checks if the download_url
value inside the Linux object is a valid URL.
In API Testing, on the Compose page, click the Add component button.
In the list of component options, click the Assert Is component.
In the Assert is window, in the Expression field, enter
payload.downloads
. This expression checks for the downloads field in the json response body.Leave the rest of the fields blank and click the checkmark to confirm the changes.
The result should look like the following:
Run the Testβ
In the Composer, click Run.

All test runs appear to the right of the Composer, under the test details and environment sections.

Review Test Resultsβ
To view your results, in the Composer, in the Test Runs list, click the name of the test. This will open the Test Report Details. For more information, see Test Outcome Report.
Compose a Request Bodyβ
There are several ways you can compose a request body in Sauce Labs API Testing, ranging from simple to complex.
note
The included examples use the POST method, but all examples can be applied to other methods.
Copy and Paste the Bodyβ
In this method, you copy an existing body and paste it into the call.
In the Composer, add a
POST
component and enter the URL and all of the required fields.Url (the url of the resource you want to test) -
https://domain/endpoint
Variable (the name of the variable that contains the response) -
payload
Mode (the response type) -
json
At the bottom of the POST request window, click Add Body.
Use the following in the Post body fields:
Content-Type -
application/json
Content (the body required in your call) -
{"method":"post","url":"http://www.testme.com/api/run/test"}
Click the Confirm changes icon and proceed with the test.
Use Variables in the Request Bodyβ
In the Composer, add a
POST
component and enter the URL and all of the required fields.Url (the url of the resource you want to test) -
https://domain/endpoint
Variable (the name of the variable that contains the response) -
payload
Mode (the response type) -
json
At the bottom of the POST request window, click Add Body.
Select the relevant Content-Type and enter the following in the Content field:
{
"user": "${user}",
"password": "${password}",
"url": "http://www.testme.com/api/run/test"
}user
andpassword
are not directly passed in the body, but they are variables defined in a data set or stored in the vault (or environments).Click the Confirm changes icon and proceed with the test.
Use a Variable from Another Callβ
Add the call to retrieve the variable from. The following is an example of a common scenario in which you need to perform a login for authentication and retrieve the authentication token required for the following call.
Url (the url of the resource you want to test) -
https://domain/login
Variable (the name of the variable that contains the response) -
payload
Mode (the response type) -
json
At the bottom of the request window, click Add Header.
In the Header window, enter the following:
Name -
Authorization
Value (from encoding
username:password
in Base64) -Basic YWRtaW46cGFzc3dvcmQ=
Click the Confirm changes icon.
The response payload from the login call will contain the desired token. Let's use the following as an example response.
Save the token as a variable using a
SET
component.Var (the variable name) -
token
Variable mode (the variable type) -
String
Value (retrieves the value from the previous payload-
${payload.access_token}
Once the token has been saved as a variable, add the second call and use that token in the request body.
Content-Type -
application/json
Content -
{"token":"${token}"}
Using an Object from Another Callβ
Using an object from another call is a more complex method. Scenarios in which you might use this method include when you need to use an object retrieved from a previous call in the body of a subsequent call.
Perform the call you retrieve the object from.
The response payload from the call:
{
"id":123,
"items":[
{
"id":11,
"name":"stuff1"
},
{
"id":12,
"name":"stuff2"
},
{
"id":13,
"name":"stuff3"
}
]
}In this example, you need the object
items
as the body in the subsequent call. So, as a second call, add aPOST
and enter the following as body:{"items":"${searchPayload.items.asJSON()}"}
Continue with the test.
Creating a New Structure to Add as a Bodyβ
This method can be used when you need to create a new structure to add as a body, using data from a previous call.
Perform the call that retrieves the data you are using. In the following example, using a
GET
returns an array of items.The response payload:
{
"items":[
{
"id":11,
"price":5.99
},
{
"id":12,
"price":6.99
},
{
"id":13,
"price":10.99
},
{
"id":14,
"price":15.99
}
]
}Create the new data structure by adding a
SET
component.Var (the variable name) -
itemsAvailable
Variable mode (the variable type) -
Language
Lang -
Javascript
Content -
payload.items.forEach(function (item) { item.currency = "$"; }); return payload;
Add the
POST
and add the new structure as thePOST
request body.Continue with the test.
Dynamic Datesβ
Instead of entering dates as static values, which may need to be updated periodically, you can create dynamic dates.
Create a Future Dateβ
Open the Composer and add a Set component.
Enter/select the following:
Var (the variable name) -
futureDate
Variable mode (the variable type) -
String
Value -
${D.format(D.plusDays(D.nowMillis(),35), 'yyyy-MM-DD')}
D.nowMillis()
- Returns the current Unix epoch in milliseconds.D.plusDays()
- Returns the provided milliseconds, plus the provided number of days (in the example, 35 days were added to today's date).D.format()
- Creates a timestamp with the given format, using the current timezone (in the example,yyyy-MM-DD
).
${D.format(D.plusDays(D.nowMillis(),35), 'yyyy-MM-DD')}
Invoke the variable in your test.
Create a Past Dateβ
Follow the steps for Create a Future Date, but replace the string with the following:
D.minusDays()
- Returns the provided milliseconds, minus the provided number of days (in the example, 35 days were subtracted from today's date).${D.format(D.minusDays(D.nowMillis(),35), 'yyyy-MM-DD')}
Create a Date with a Time Zoneβ
To create a date based on a specified time zone:
D.format()
- Creates a timestamp with the given format, based on the provided time zone ID (the example uses the same date as before, but usesNew York
as the time zone).${D.format(D.plusDays(D.nowMillis(),35), 'yyyy-MM-DD','America/New_York')}
Convert a Timestamp in Unix Time in Millisecondsβ
To convert a timestamp from a payload response to milliseconds:
D.parse()
- Parses the provided timestamp and converts it to milliseconds.${D.parse(1649094357)}
For more information, see Expression Language Extensions.
Integration Testsβ
Integration testing is critical for creating a strong API testing strategy. An integration test allows you to create end-to-end tests that resemble common user flows. While only testing individual endpoints is a good start, this method will miss a large number of problems that occur when all services need to work together.
Token-based Authentication APIβ
Company A has an authentication server. This server, when given the proper user credentials, returns an authentication token. This token is required for all other calls throughout the platformβs API environment. Without this first API call, none of the other API calls can work.
To get the token, make a
POST
call to the authorization server.The request body is the user ID and password. Given proper credentials, the authentication server will return a user token.
Use this token to make further calls to the application.
Add a
Set (variable)
component by entering/selecting the following in the Composer:Var (the variable name) -
access_token
Variable mode (the variable type) -
String
Value -
${authPayload.access_token}
This step takes the
access_token
variable in theauthPayload
response, and sets it asaccess_token
; the response body from the original post call was saved to a variable calledauthPayload
. The access key for the token isaccess_token
, which can be found by callingauthPayload.access_token
.note
The dollar sign and brackets are necessary when referencing variables so that Sauce Labs API Testing knows to interpret whatβs between the brackets instead of using it literally.
Variables are used to store data temporarily for a test, but you can use the Sauce Labs API Testing Vault for permanent variables. For more information, see Creating Reusable Variables and Snippets with the Vault).
Make follow-up calls.
In the following example, the API has a cart function that requires a user token to add items to a cart or view items currently in the cart. Use a
PUT
request to the cart endpoint to update the cart. Use the access token granted by the authentication server to add items to a cart by setting thetoken
header to${access_token}
.You can also reuse access tokens:
Test Interactions between Endpointsβ
In the following example, there is an API endpoint that produces an array of all the available products and another endpoint that shows the details of a specific product based on its ID.
http://demoapi.apifortress.com/api/retail/product
http://demoapi.apifortress.com/api/retail/product/${id}
To create an integration test to test the interaction between the endpoints:
Call the product listing endpoint and assign the response to the
productsPayload
variable.Add an
each
assertion and reference theproductsPayload.products
object.note
In a scenario in which the response contains many products, it may be useful to pick a few at random by using
pick(n)
.Test the response payload for the endpoint.
Add a new
Set (variable)
assertion to set theid
variable as every singleproductsPayload.product
that is returned. In the following example, the string is${_1.id}
. The system uses_1
automatically when recognizing a subroutine, which makes it easier when there are multiple sub-levels.Create a
GET
request to the product details endpoint, using the newid
variable as the id parameter. Variables last through the entire test unless overwritten.Test the response payload for the endpoint.
<get url="http://demoapi.apifortress.com/api/retail/product" params="[:]" var="productsPayload" mode="json">
<header name="key" value="ABC123"/>
</get>
<assert-is expression="productsPayload" type="array" mode="all" comment="payload must be an array"/>
<comment>
<![CDATA[pick randomly 5 items from the payload response]]>
</comment>
<each expression="productsPayload.pick(5)">
<comment>
<![CDATA[product id is: ${_1.id} and product name is: ${_1.name}]]>
</comment>
<assert-is expression="_1.id" type="integer" mode="all" comment="id must be an integer value"/>
<set var="id" value="${_1.id}" lang="java"/>
<assert-exists expression="_1.name" mode="all" comment="name must exists"/>
<assert-is expression="_1.price" type="float" mode="all" comment="price must be a float number"/>
<assert-exists expression="_1.category" mode="all" comment="category must exists"/>
<assert-exists expression="_1.description" mode="all" comment="description must exists"/>
<assert-is expression="_1.quantity" type="integer" mode="all" comment="quantity must be an integer value"/>
<assert-greater expression="_1.quantity" value="0" type="integer" mode="all" comment="quantity must be greater than 0"/>
<assert-is expression="_1.imageURL" type="url" mode="all" comment="imageURL must be a valid url value"/>
<assert-is expression="_1.color" type="array" mode="all" comment="color must be an array"/>
<assert-exists expression="_1.createdAt" mode="all" comment="createdAt must exists"/>
<assert-exists expression="_1.updatedAt" comment="updateAt must exists"/>
<comment>
<![CDATA[get product details]]>
</comment>
<get url="http://demoapi.apifortress.com/api/retail/product/${id}" params="[:]" var="productPayload" mode="json">
<header name="key" value="ABC123"/>
</get>
<assert-exists expression="productPayload" mode="all" comment="payload must exist, if not, test does not need to be executed" stoponfail="true"/>
<comment>
<![CDATA[product id is: ${productPayload.id} and product name is: ${productPayload.name}]]>
</comment>
<assert-equals expression="productPayload.id" value="${id}" type="integer" mode="all" comment="id is the same as the one from the previous call"/>
<assert-is expression="productPayload.id" type="integer" mode="all" comment="id must be an integer value"/>
<assert-exists expression="productPayload.name" mode="all" comment="name must exists"/>
<assert-is expression="productPayload.price" type="float" mode="all" comment="price must be a float number"/>
<assert-exists expression="productPayload.category" mode="all" comment="category must exists"/>
<assert-exists expression="productPayload.description" mode="all" comment="description must exists"/>
<assert-is expression="productPayload.quantity" type="integer" mode="all" comment="quantity must be an integer value"/>
<assert-greater expression="productPayload.quantity" value="0" type="integer" mode="all" comment="quantity must be greater than 0"/>
<assert-is expression="productPayload.imageURL" type="url" mode="all" comment="imageURL must be a valid url value"/>
<assert-is expression="productPayload.color" type="array" mode="all" comment="color must be an array"/>
<each expression="productPayload.color">
<assert-exists expression="_2" mode="all" comment="color array should contain some values"/>
<assert-in expression="_2" value="['yellow','blue','red','green','brown','orange','gray','pink','black','white']" mode="all" comment="colors must be the expected one"/>
</each>
<assert-exists expression="productPayload.createdAt" mode="all" comment="createdAt must exists"/>
<assert-exists expression="productPayload.updatedAt" comment="updateAt must exists"/>
</each>
Testing Metricsβ
An HTTP response is made of a payload, but also contains contextual information. You can use Sauce Labs API Testing to test the entire response envelope.
When you're making an HTTP request in the composer, you're providing a variable name. That variable will host the entire response payload. For example, if payload
is the name of that variable, when the operation completes, another variable called <variable_name>_response
is also created.
Therefore various pieces of information such as HTTP header and metrics are contained in the variable payload_response
.
By referencing the payload_response.statusCode
expression you can access the status code.
Exampleβ
In this example, you want to run a branch of code when the status code is 400
:

You can use multiple if
conditions to check the status codes you need to check, which can be helpful when creating positive and negative tests.

Exampleβ
To check that a resource shouldn't be cached:

Performance Metricsβ
You can create specific assertions to verify performance metrics.
Exampleβ
The following is an example in Code view.
<assert-less expression="payload_response.metrics.latency" value="350" type="integer"/>
<assert-less expression="payload_response.metrics.fetch" value="350" type="integer"/>
<assert-less expression="payload_response.metrics.overall" value="550" type="integer"/>
latency
is the time to first byte.fetch
is the total download time of the payload.overall
is fetch and latency combined.
The following is the same example, but in Visual view:

Improving Metricsβ
The performance of the API can be mission critical in some cases, and cataloging metrics can be as important as collecting them.
The classic approach of creating big tables of HTTP hits with the actual URL being called (and its performance) is certainly accurate, but it's far from being easy to review because URLs contain variables and hardly represent what the action was about.
Sauce Labs API Testing, as a default, works in this classic way, but also gives you the ability to change the footprint of requests based on your organization needs.
Exampleβ
The following example includes a route with a parameter:
http://www.whereever.com/[id]/details
Each individual REST
run for this route will produce a new line in the metrics view:
http://www.whereever.com/1/details
http://www.whereever.com/2/details
http://www.whereever.com/3/details
http://www.whereever.com/4/details
...
To produce a single endpoint for reporting from each one of these calls, you can use a footprint.
To reconfigure the footprint, in the test, add a config
component to the I/O component:

The config
component has two fields:
- Name - The name you want to assign. In this case, you MUST enter
footprint
. - Value - The value for the configuration component.
To set up a footprint, enter the URL that's in the I/O component. Any parameterized portion of the URL must be wrapped in square brackets.
The value in this example would be:
http://www.wherever.com/whatever/[id]/details
For each endpoint, you can use more square brackets, one for each variable that could assume multiple values:
http://www.whereever.com/[whatever]/[id]/details/[colors]/whatever
When you write the value of the config, for the static part of the endpoint, you can also call a variable as in any I/O operation:
${protocol}/${domain}/[whatever]/[id]/details/[colors]/whatever
Terminologyβ
Visual View and Code Viewβ
This toggle switches between the Visual and Code views in the Composer. You can make calls and add assertions for testing your APIs, and insert variables wherever needed. You can use either, depending on which you're more comfortable with.
Visual Viewβ
Guides you through creating API tests using automated real-time suggestions via predictive text. No coding experience is required.
Code Viewβ
Enables you to write tests here from scratch, if you feel more comfortable working in code.
Add Componentβ
This button displays all available assertion components, I/O components, and logical components.

If a component is not valid for the operation you are conducting, it will not be made available to help avoid mistakes. For instance, if you donβt add a POST
first, you cannot add a POST
Body or POST
Param.
note
Sauce Labs free trials may not give you access to all available components.
Transform Componentβ
Transforms an existing component into another component of the same type.

Delete Componentβ
Deletes a selected component from the test while using Visual view.

Invoke Snippetβ
Allows you to use a previously created code snippet stored in The Vault.

Export Snippetβ
Allows you to export a selected code snippet to the vault in order to be re-used later, or in another test.

Save Testβ
Saves your progress.

Run Testβ
Executes a test.

Input Setsβ
Displays the Input Set view where you can store input data sets to reuse within the specific test you're working on.
There are two types of input data sets you can use:
- Global Parameters - Variables that are available within a test, valid for that specific test only.
- Input Set - Group of input variables representing a scenario, valid for that specific test only. The test will be executed once for each input set, overriding the variable values into your test.
Input Set with Visual View | ![]() |
Input Set with Code View | ![]() |
Unit Viewβ
These buttons switch between the Input Set and Unit views.
