Your Gateway to Exciting Quality Assurance!

How to set up automated API tests

API tests
Automated testing is crucial for modern software development. By setting up automated tests for your APIs, you can ensure that your APIs continue to function as expected after changes and catch bugs early. This allows you to develop faster and with more confidence.
In this comprehensive guide, we'll explore how to set up automated API tests from scratch. We'll cover:
  • The benefits of automated API testing
  • An overview of APIs and how they work
  • Determining what to test in your APIs
  • Choosing an API testing framework
  • Writing API tests
  • Creating reusable modules
  • Integrating tests into your development workflow
  • Executing tests and generating reports

The Benefits of Automated API Testing

Automated API testing provides many advantages over manual testing:
  • Finds bugs early - Tests can be run during development, catching bugs before code is deployed. This is cheaper than finding bugs later.
  • Saves time - Automated tests run much faster than manual tests. No need to manually step through API workflows.
  • Improves coverage - Automated tests can cover many use cases, scenarios, and edge cases. Far more than practical with manual testing.
  • Increases confidence - Automated tests provide a safety net so developers can refactor/update code quickly and fearlessly.
  • Facilitates collaboration - API tests define how code should work. Useful documentation for other team members.
  • Simplifies regression testing - Tests can easily be re-run to check for regressions after changes. No need to re-test manually.

Well-designed API tests maximize these benefits. Let's look at some key considerations when setting them up.

An Overview of APIs and How They Work

First, a quick API overview for context.
APIs (Application Programming Interfaces) allow different software systems to communicate with each other. APIs work by exposing endpoints that can be called to retrieve data or perform actions.
For example, a weather API might expose an endpoint like:
/current-weather?zipCode=12345
Which returns the current weather for a given zip code:
{
  "temp": "29C",
  "condition": "Sunny"
}
APIs use standard formats like JSON or XML to exchange data. Requests are made using HTTP methods like GET, POST, PUT, and DELETE.
Automated API tests verify everything works as expected:
  • URLs resolve to the correct endpoints
  • Parameters return the right data
  • HTTP response codes signal success/failure appropriately
  • The response body contains expected values

Thorough API testing verifies all pieces work together seamlessly.

Determining What to Test in Your APIs

Before writing automated tests, determine what aspects of your APIs need testing.
As a rule of thumb, you should test:
  • Endpoints - Verify all endpoints are reachable and return the correct HTTP response codes.
  • Parameters - Try different parameter values to ensure APIs handle them properly.
  • Authentication - Test with both valid and invalid credentials.
  • Data formats - If APIs accept multiple formats (like JSON and XML), test with each.
  • Error handling - Pass invalid data to confirm appropriate errors are thrown.
  • Edge cases - Think of edge cases like empty inputs, zero values, extremely large numbers etc. And write tests to verify behavior under edge cases.

Document how endpoints should work and what responses they should return. Review API documentation to ensure tests cover all intended functionality.
Focusing tests on key endpoints, workflows, and potential failure points provides excellent coverage without excessive maintenance.

Choosing an API Testing Framework

Many libraries and frameworks are available for API test automation. Some popular options:

Postman

A very popular and full-featured API testing tool. Provides a graphical interface for constructing requests and asserting responses. Test scripts can be exported and run from the command line using Newman. Works with any language.

REST Assured

A Java library designed specifically for REST/HTTP API testing. Provides methods for validating status codes, headers, body content etc. Integrates with test runners like JUnit and TestNG.

SuperTest

A minimal Node.js library for API testing. Makes it easy to send test requests and expect specific responses. Integrates nicely with frameworks like Mocha.

karate

Open source API test framework for Java/JavaScript. Expressive BDD-style syntax for validating all aspects of HTTP interactions. Can run tests in parallel for speed.

Robot Framework

A generic open source automation framework. Can use the RequestsLibrary to test APIs in Python. Or HTTPLibrary for Java.

Consider which language your tests will be written in and any other frameworks/tools in your stack when choosing.

Writing Effective API Tests

With a testing framework selected, we can start writing API tests:

1. Send Requests
First, send a request to the endpoint we want to test:
// SuperTest example 

const request = require('supertest');

const api = request('https://api.example.com'); 

describe('GET /users', () => {

  it('responds with a 200 status code', async () => {

    const response = await api.get('/users');
  
  });

});
The request method matches the HTTP verb. We can pass parameters, headers, and other options.

2. Validate Status Code
Next, assert the response status code is as expected:
it('responds with a 200 status code', async () => {

    const response = await api.get('/users');

    expect(response.statusCode).toEqual(200);

});
Status codes like 200, 404, 500 tell us a lot about the request's success/failure.

3. Verify Response Body
Then assert the response body contains expected values:
it('returns expected user properties', async () => {

  const response = await api.get('/users/123');

  expect(response.body.id).toEqual(123);

  expect(response.body.name).toEqual('John'); 

});
Match properties against hardcoded expected values or variables.

4. Parameterize Tests
Parameterize tests to easily pass different values:
it('accepts a numeric userId parameter', async () => {

  const ids = [1, 200, 9999];

  ids.forEach(id => {

    const response = await api.get(`/users/${id}`);
    
    expect(response.statusCode).toEqual(200);

  });

});
This loops through different IDs to validate they work properly.

5. Group Related Tests
Use describes and contexts to group related tests:
describe('GET /users', () => {

  describe('by ID', () => {
  
    // ID parameter tests here
  
  });

  describe('search', () => {
  
    // name parameter tests here
  
  });

});
This structures tests nicely for readability.

6. Handle Errors
Use try/catch blocks to handle errors gracefully:
it('returns a 404 for non-existent user', async () => {

  try {

    const response = await api.get('/users/9999999');

  } catch (error) {

    expect(error.statusCode).toEqual(404);

  }

});
This validates the endpoint properly returns a 404 for a missing user.

Creating Reusable Test Modules

To avoid duplication, we can create reusable modules for things like:
  • Test setup/teardown
  • Common requests
  • Response assertions
  • Data generators
  • Custom expect matchers

For example:
users.js
// Reusable user generator

const { faker } = require('@faker-js/faker');

const generateRandomUser = () => {

  return {
    id: faker.datatype.number(),
    name: faker.name.findName(),
    email: faker.internet.email() 
  };

};

module.exports = { generateRandomUser };
Which can be imported anywhere:
// tests.js 

const { generateRandomUser } = require('./users');

it('allows creating a new user', async () => {

  const newUser = generateRandomUser();
  
  // ... send POST request with newUser ...

});
This makes tests more readable and maintainable.

Integrating Tests into Development

Automated API tests provide the most value when run continuously during development. Common ways to integrate tests:
  • Pre-commit hooks - Run tests as a pre-commit hook to prevent broken code from being committed.
  • CI pipelines - Run tests on every push to catch regressions immediately. Fail build if tests don't pass.
  • Alongside API development - Run tests locally each time an endpoint is updated. Provides quick feedback.
  • Scheduled execution - Run test suites on a scheduled cadence like overnight or weekly. Catch issues that slip through the cracks.
  • On-demand execution - Run tests manually or on-demand as needed before releases.

Focus on getting fast feedback by running tests early and often during the development process.

Executing Tests and Generating Reports

Most testing frameworks provide built-in methods for running tests and generating reports:

Command line
Frameworks like Jest, Mocha, etc allow running tests from the command line:
mocha tests/
Most also support options like:
  • Running subsets of tests by name
  • Running in parallel for speed
  • Outputting results to a file

CI/CD pipelines
Testing frameworks integrate nicely into CI/CD pipelines. Tests can be run as a step in the pipeline using the CLI commands.

HTML reports
Many frameworks can generate HTML test reports:
Reports give an overview of how many tests passed/failed etc.

Logging
Frameworks can integrate with logging libraries like log4j, log4js, etc. Logs provide visibility into test execution.
Focus test execution on maximizing confidence that releases are high quality. Setting up robust automated API testing provides huge quality and velocity benefits.

Key steps include:
  • Determining test coverage goals
  • Selecting a good testing framework
  • Writing well-structured tests focused on common cases and edge cases
  • Creating reusable modules and helpers
  • Integrating tests into the development process
  • Executing tests and producing reports

Approaching API testing systematically helps ensure a high level of API quality and reliability after each release. Testing early, often, and thoroughly gives developers confidence their APIs work exactly as intended.