Skip to main content

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

Creating a Test with the Composer

Create a Test

  1. In Sauce Labs, click API Testing.

    Navigating to API Testing
  2. On the Projects page:

    • If you have no tests or projects yet, in the Write your own test box, click Use Composer.

      Navigating to the 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.

      Navigating to the New Test window
  3. In the New Test box, enter a test name, test description (optional), and tags (optional), and then click Create Test.

    New Test window
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.

Navigating to the test editor

Add Test Components

When test components are combined, they act as our test logic. See the following pages for more information about the components types available in API Testing:

Add an I/O Request Test Component

To create a simple GET request and validate that response is correct:

  1. In API Testing, on the Compose page, click Add child component.

    Navigating to the Add component screen
  2. In the list of component options, click the GET component.

    Navigating to the GET request window
  3. In the GET request window, in the Url field, enter https://</span>api.us-west-1.saucelabs.com/rest/v1/public/tunnels/info/versions.

    This endpoint will return a JSON response body.

  4. In the Variable field, enter payload. This variable stores the response, so it can now be referred to as payload.

    Editing in the GET request window
  5. Leave the rest of the fields blank and then click Save Changes.

    The result should look like the following:

    What the GET request should look like

For more information, see I/O Request Test Components.

Add an Assertion Component

  1. In API Testing, on the Compose page, click Add child component.

    Navigating to the Add component screen
  2. In the list of component options, click the Assert Exists component.

    Navigating to the Assert exists window
  3. In the Assert exists window, in the Expression field, enter payload.downloads. This expression checks for the downloads field in the json response body.

  4. Leave the rest of the fields blank and click Save Changes.

    Confirm changes
  5. The result should look like the following:

    What the Assert request should look like

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.

  1. In API Testing, on the Compose page, click Add child component.

  2. In the list of component options, click the Assert Is component.

  3. In the Assert is window, in the Expression field, enter payload.downloads. This expression checks for the downloads field in the json response body.

  4. Leave the rest of the fields blank and click Save Changes.

    Confirm changes
  5. The result should look like the following:

    What the Assert request should look like

Run the Test

In the Composer, click Run.

Save and Run icons in the Composer

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

Test Runs in the Composer

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.

  1. 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
  2. Below the POST request, click Add Child Component and then click Request Body.

    Navigating to the Post body window
  3. Use the following in the Post body fields:

    • Content-Type - application/json

    • Body (the body required in your call) - {"method":"post","url":"http://www.testme.com/api/run/test"}

      The Post body window
  4. Click Save Changes and proceed with the test.

Use Variables in the Request Body

  1. 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
  2. Below the POST request, click Add Child Component and then click Request Body.

    Navigating to the Post body window
  3. Select the relevant Content-Type and enter the following in the Body field:

    {
    "user": "${user}",
    "password": "${password}",
    "url": "http://www.testme.com/api/run/test"
    }
    The Post body window

    user and password are not directly passed in the body, but they are variables defined in a data set or stored in the vault (or environments).

  4. Click Save Changes and proceed with the test.

Use a Variable from Another Call

  1. 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
  2. Below the request, click Add Child Component and then click Request Header.

    Navigating to the Header window
  3. In the Header window, enter the following:

    • Name - Authorization

    • Value (from encoding username:password in Base64) - Basic YWRtaW46cGFzc3dvcmQ=

      The Header window
  4. Click Save Changes.

  5. The response payload from the login call will contain the desired token. Let's use the following as an example response.

    An example response
  6. Save the token as a variable using a SET component.

    • Variable (the variable name) - token

    • Mode (the variable type) - String

    • Value (retrieves the value from the previous payload- ${payload.access_token}

      The SET Variable window
  7. 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

    • Body - {"token":"${token}"}

      POST request body

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.

  1. Perform the call you retrieve the object from.

    GET request window

    The response payload from the call:

    {
    "id":123,
    "items":[
    {
    "id":11,
    "name":"stuff1"
    },
    {
    "id":12,
    "name":"stuff2"
    },
    {
    "id":13,
    "name":"stuff3"
    }
    ]
    }
  2. In this example, you need the object items as the body in the subsequent call. So, as a second call, add a POST and enter the following as body:

    {"items":"${searchPayload.items.asJSON()}"}
    POST request body
  3. 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.

  1. Perform the call that retrieves the data you are using. In the following example, using a GET returns an array of items.

  2. The response payload:

    {
    "items":[
    {
    "id":11,
    "price":5.99
    },
    {
    "id":12,
    "price":6.99
    },
    {
    "id":13,
    "price":10.99
    },
    {
    "id":14,
    "price":15.99
    }
    ]
    }
  3. Create the new data structure by adding a SET component.

    • Variable (the variable name) - itemsAvailable

    • Mode (the variable type) - Language

    • Language - Javascript

    • Body - payload.items.forEach(function (item) { item.currency = "$"; }); return payload;

      The SET Variable window
  4. Add the POST and add the new structure as the POST request body.

    The POST body window
  5. 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

  1. Open the Composer and add a Set component.

  2. Enter/select the following:

    • Variable (the variable name) - futureDate

    • Mode (the variable type) - String

    • Value - ${D.format(D.plusDays(D.nowMillis(),35), 'yyyy-MM-DD')}

      The SET Variable window
    • 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')}
  3. 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 uses New 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.

Generating Test Data

If your API or test requires random names, emails, or different types of input data, you can generate those directly in Sauce Labs API Testing. You can directly reference the method in your variable, API call, or anywhere in the test where you can enter the ${F.<methodName()>} syntax.

  • F.fullName() - Generates a full name
  • F.firstName() - Generates a first name
  • F.lastName() - Generates a last name
  • F.emailAddress() - Generates an email address
  • F.password(<minimumLength,maximumLength,includeUppercase,includeSpecial,includeDigit>) - Generates a password
  • F.creditCardNumber() - Generates a credit card number
  • F.creditCardExpiry() - Generates a credit card expiration date
  • F.integer(<min,max>) - Generates an integer

For the full list of methods, see Test Data Methods.

These methods can be used anywhere you can write a variable, such as inside a Set (variable) or in any part of the request (body, header, param, etc.).

Set (variable)

Set (variable) allows you to create variables or more structured data.

To create a single variable:

  • Variable: The name to assign the variable.
  • Mode: String
  • Value: The method to use to generate data. For example, ${F.fullName()} will generate a random full name.
Full name generation

To create an array of data:

  • Variable: The name to assign the variable.
  • Mode: Data
  • Data: The JS function for creating an array. For example, new Array(5).fill(0).map(_ => F.streetAddress()) generates an array with five random addresses.
Data array generation

To create an object of data:

  • Variable: The name to assign the variable.
  • Mode: Language
  • Language: Template
  • Body: The object to generate with the required methods.
{
"name": "${F.firstName()}",
"last name": "${F.lastName()}",
"address": "${F.streetName()}",
"profession": "${F.profession()}",
"mobile phone": "${F.mobile()}",
"email": "${F.emailAddress()}"
}
Personal data generation

Body

Any of the following methods can be used in a request body.

  • Content-Type: The content-type of the body (application/json in this example).
  • Body: The body of the request.
{
"name": "${F.firstName()}",
"last name": "${F.lastName()}",
"city": "${F.city()}",
"profession": "${F.profession()}"
}
Request body data

In this example we use a POST body, but this can be applied in all REST methods. Similarly, these methods can also be used as params.

  • Name: The name of the param.
  • Value: The value of the param. For example, ${F.creditCardNumber()}.
Request body param data

These examples are of the most common places where you may need to generate data, but these methods can be added anywhere you can use a variable.

Test Data Methods

Addresses and Countries

  • F.streetName() - Generates a street name
  • F.streetAddressNumber() - Generates an address number
  • F.streetAddress() - Generates a street and address number. If secondary is specified, this method provides an apartment number.
  • F.secondaryAddress() - Generates an apartment number
  • F.zipCode() - Generates a ZIP code. Valid only for US states.
  • F.streetSuffix() - Generates a street suffix
  • F.citySuffix() - Generates a city suffix
  • F.cityPrefix() - Generates a city prefix
  • F.city() - Generates a city name
  • F.state() - Generates a state/province
  • F.buildingNumber() - Generates a build number
  • F.country() - Generates a country
  • F.countryCode() - Generates a country code
  • F.countryCodeSL() - Generates a country code in small letters

People and Identity

  • F.fullName() - Generates a full name
  • F.firstName() - Generates a first name
  • F.lastName() - Generates a last name
  • F.profession() - Generates a profession
  • F.timeZone() - Generates a time zone
  • F.phone() - Generates a phone number
  • F.mobile() - Generates a mobile number

Internet

  • F.emailAddress() - Generates an email address. Note: These email addresses are randomly generated with real domains. Please be careful if you are using this in a test as there is a chance that some of them could be real email addresses.
  • F.domainName() - Generates a domain name
  • F.domainWord() - Generates a word
  • F.domainSuffix() - Generates a suffix
  • F.url() - Generates a URL
  • F.password(<minimumLength,maximumLength,includeUppercase,includeSpecial,includeDigit>) - Generates a password. For example, password(5,10,true,false, true).

Credit Card

  • F.creditCardNumber() - Generates a credit card number
  • F.creditCardExpiry() - Generates a credit card expiration date
  • F.creditCardType() - Generates a credit card type

Products

  • F.productName() - Generates a product name
  • F.price() - Generates a price

Companies

  • F.companyName() - Generates a company name
  • F.suffix() - Generates a company suffix

Random Numbers

  • F.integer(<min,max>) - Generates an integer. For example, integer(2,20)
  • F.decimal(<min,max,maxdecimals>) - Generates a decimal number. For example, integer(0,2,2)
  • F.uuid() - Generates a unique identifier

Boolean

  • F.bool() - Generates a boolean value

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.

  1. To get the token, make a POST call to the authorization server.

    POST request to authentication server

    The request body is the user ID and password. Given proper credentials, the authentication server will return a user token.

    The user token

    Use this token to make further calls to the application.

  2. Add a Set (variable) component by entering/selecting the following in the Composer:

    • Variable (the variable name) - access_token

    • Mode (the variable type) - String

    • Value - ${authPayload.access_token}

      Setting the variable

    This step takes the access_token variable in the authPayload response, and sets it as access_token; the response body from the original post call was saved to a variable called authPayload. The access key for the token is access_token, which can be found by calling authPayload.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).

  3. 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 the token header to ${access_token}.

    Setting the token header

    You can also reuse access tokens:

    Reusing 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:

  1. Call the product listing endpoint and assign the response to the productsPayload variable.

  2. Add an each assertion and reference the productsPayload.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).

  3. Test the response payload for the endpoint.

  4. Add a new Set (variable) assertion to set the id variable as every single productsPayload.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.

    Testing interactions between endpoints
  5. Create a GET request to the product details endpoint, using the new id variable as the id parameter. Variables last through the entire test unless overwritten.

  6. Test the response payload for the endpoint.

    Testing the response payload
- id: get
children:
- id: header
name: key
value: ABC123
url: http://demoapi.apifortress.com/api/retail/product
var: productsPayload
mode: json
- id: if
children:
- id: comment
text: endpoint is not working fine, test will be stopped
- id: flow
command: stop
expression: productsPayload_response.statusCode!='200'
- id: assert-is
expression: productsPayload
comment: payload must be an array
type: array
- id: each
children:
- id: comment
text: "product id is: ${_1.id} and product name is: ${_1.name}"
- id: assert-is
expression: _1.id
comment: id must be an integer value
type: integer
- id: set
var: id
mode: string
value: ${_1.id}
- id: assert-exists
expression: _1.name
comment: name must exists
- id: assert-is
expression: _1.price
comment: price must be a float number
type: float
- id: assert-exists
expression: _1.category
comment: category must exists
- id: assert-exists
expression: _1.description
comment: description must exists
- id: assert-is
expression: _1.quantity
comment: quantity must be an integer value
type: integer
- id: assert-greater
expression: _1.quantity
comment: quantity must be greater than 0
value: 0
- id: assert-is
expression: _1.imageURL
comment: imageURL must be a valid url value
type: url
- id: assert-is
expression: _1.color
comment: color must be an array
type: array
- id: each
children:
- id: assert-exists
expression: _2
comment: color array should contain some values
- id: assert-in
expression: _2
comment: colors must be the expected one
value:
- yellow
- blue
- red
- green
- brown
- orange
- gray
- pink
- black
- white
expression: _1.color
- id: assert-exists
expression: _1.createdAt
comment: createdAt must exists
- id: assert-exists
expression: _1.updatedAt
comment: updateAt must exists
- id: comment
text: get product details
- id: get
children:
- id: header
name: key
value: ABC123
url: http://demoapi.apifortress.com/api/retail/product/${id}
var: productPayload
mode: json
- id: if
children:
- id: comment
text: endpoint is not working fine, test will be stopped
- id: flow
command: stop
expression: productPayload_response.statusCode!='200'
- id: assert-exists
expression: productPayload
comment: payload must exist, if not, test does not need to be executed
- id: comment
text: "product id is: ${productPayload.id} and product name is:
${productPayload.name}"
- id: assert-is
expression: productPayload.id
comment: id must be an integer value
type: integer
- id: assert-exists
expression: productPayload.name
comment: name must exists
- id: assert-is
expression: productPayload.price
comment: price must be a float number
type: float
- id: assert-exists
expression: productPayload.category
comment: category must exists
- id: assert-exists
expression: productPayload.description
comment: description must exists
- id: assert-is
expression: productPayload.quantity
comment: quantity must be an integer value
type: integer
- id: assert-greater
expression: productPayload.quantity
comment: quantity must be greater than 0
value: 0
- id: assert-is
expression: productPayload.imageURL
comment: imageURL must be a valid url value
type: url
- id: assert-is
expression: productPayload.color
comment: color must be an array
type: array
- id: each
children:
- id: assert-exists
expression: _2
comment: color array should contain some values
- id: assert-in
expression: _2
comment: colors must be the expected one
value:
- yellow
- blue
- red
- green
- brown
- orange
- gray
- pink
- black
- white
expression: productPayload.color
- id: assert-exists
expression: productPayload.createdAt
comment: createdAt must exists
- id: assert-exists
expression: productPayload.updatedAt
comment: updateAt must exists
expression: productsPayload.pick(5)

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:

Testing metrics - example

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.

Using multiple if conditions

Example

To check that a resource shouldn't be cached:

Checking 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.

- id: assert-less
expression: payload_response.metrics.latency
value: 350
- id: assert-less
expression: payload_response.metrics.fetch
value: 350
- id: assert-less
expression: payload_response.metrics.overall
value: 450
  • 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:

An Assert-Less component 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:

Reconfiguring a footprint

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.
Test Composer Visual view

Code View

Enables you to write tests here from scratch, if you feel more comfortable working in code.
Test Composer Code view

Add Child Component

This button displays all available assertion components, I/O components, and logical components.

Add Component

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.

Component Options

Click Edit to modify an existing component, or use the dropdown menu next to Edit to perform the actions shown below.

Component Options

Save

Saves your progress.

Save

Publish

Publishes your test.

Publish

Clear

Clears the most recent unpublished changes made to your test.

Clear

Run

Executes a test.

Run

Input Sets

Displays the Input Set view where you can store input data sets to reuse within the specific test you're working on.
Input Sets

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 Visual View
Input Set with Code ViewInput Set Code View

Unit View

These buttons switch between the Input Set and Unit views.


Unit View

More Information