I’ve previously shared how to build resilient APIs with chaos engineering and security hacking with the Big List of Naughty Strings. I’ve also broken some hearts, so I have some experience with breaking things. In this post, let’s go beyond basic test automation in Postman, and learn why intentionally trying to break things increases the resilience of your applications. We’ll also explore some common negative test scenarios.
Happy path vs. unhappy path
During product design and development, stakeholders focus on intended user behavior. Product owners describe this happy path with business requirements. Engineers push code to enable users to do these things. A user who demonstrates this expected behavior follows the happy path.
These are examples of happy paths:
- User creates a new account
- User signs in with their new credentials
- User retrieves information about their account
But what happens when there’s unintended behavior, like when a user submits invalid inputs? These unhappy path scenarios are often neglected in design and development. But with enough traffic, unexpected inputs will inevitably come from both well-intentioned and malicious users.
These are examples of unhappy paths:
- User creates a new account without providing all required inputs
- User signs in with invalid credentials
- User pastes a SQL query that gets executed (maliciously or unintentionally)
A strong test plan covers both happy and unhappy paths. Positive test cases describe happy path scenarios, where errors result in failed tests. Negative test cases describe unhappy path scenarios, where expected errors result in passed tests.
Trying to break the Unbreakable API
In this “How to Break an API” livestream with my colleagues Trent McCann, quality engineering manager, and Evan Lindsey, lead SDET, I tried to break the Unbreakable API. This API was engineered by Evan to demonstrate the importance of validating inputs and handling errors.
Typically, architects and engineers of an API are very familiar with the vulnerabilities of that API. However, they’re focused on enabling happy path user stories. They may not be thinking through all possible scenarios a user might encounter when interacting with the API. In the next section, let’s explore some conventional ways to break an API.
A recommended testing flow
Unbreakable API Lite is a simplified version of Unbreakable API used in the “How to Break an API” livestream. Instead of managing authorization for multiple roles, the role of admin is removed and employee is renamed to user. In Unbreakable API Lite, once you create a user and set the returned token, you will have access to all available endpoints.
The collection Testing Flow for Lite includes examples of positive and negative test scenarios for the API referenced in Unbreakable API Lite. For engineers, Testing Flow for Lite can be a guide for input validation and error handling. And for testers, it can be a recipe for mischief.
Try it out in Postman
- Fork the collection: Fork the collection Testing Flow for Lite to your workspace. You may need to enable your public profile if you haven’t already.
- Create a new user: Find the User create request, and update the values under the Body tab. Hit Send to create the new user, and also set a collection variable called userToken that can be used in subsequent calls.
- Step through the collection: Explore the positive and negative test scenarios outlined in the remaining folders. Under the Authorization tab, notice the authorization method used for each request. Under the Pre-request Script and Tests tabs, notice code that runs before and after you send each request.
- Run the collection automatically: This collection can also be run to automate your testing flow. This is done using the Runner in Postman, Newman from the command line, or Monitors on Postman servers. Remember to set up a new, unique user under the User create request before running the collection in its entirety.
Positive and negative testing
The examples in the Positive folder of Testing Flow for Lite describe happy path scenarios, such as:
-
- Retrieve all movies
- Create a new movie
- Retrieve the new movie by ID
For positive test cases, you write a test asserting a successful response like receiving a 200 OK, or similar, HTTP status code. If there are errors in those assertions, the test should fail. For example, to test the endpoint of a user creating a new movie, write a Postman test and assertion like this:
pm.test("Status code is 200", function () { pm.response.to.have.status(200); });
The examples in the Negative folder describe unhappy path scenarios, such as:
- Retrieve all movies, using an authorization token when not needed
- Create a new movie, using an invalid authorization token
- Delete the movie, when cascade is not set on database cleanup
For negative test cases, you write tests expecting certain errors. If your application doesn’t return expected errors or swallows exceptions, your application isn’t handling unexpected input gracefully. In these cases, your tests should fail if you don’t see the expected errors.
For example, what happens when a user creates a new movie and submits an invalid authorization token? We expect the server to return a 401 Unauthorized, or similar, HTTP status code. To test that case, send a token that you know is invalid, and write a Postman test and assertion like this:
pm.test("Status code is 401", function () { pm.response.to.have.status(401); });
The importance of negative testing
With negative testing, you are asserting the application accommodates unexpected user behaviors. When unexpected user behavior crashes the whole system, engineering rushes to put fail-safes in place, adding tests to ensure they are handling that exact scenario correctly going forward.
With negative testing, you’re proactively exploring these edge cases to increase the resilience of your APIs in production. It’s important to establish a workflow and foster a supportive organizational culture to do this, before your system fails.
Let us know your favorite way to break an API in the comments below. And level up your testing in Postman with these additional resources:
- Test examples in Postman public workspace
- Acing your API tests — what you need to know for test automation blog and examples
- Chaos engineering to increase resilience blog and recipe
- How to break an API livestream
- Security hacking with the Big List of Naughty Strings livestream
Technical review by Evan Lindsey.
Postman provides a wide range of functions and features to assist with API development, testing, and collaboration. Here are some commonly used functions in Postman:
-
Creating and Managing Requests: Postman allows you to create API requests by specifying the request method, URL, headers, parameters, and body. You can manage and organize requests within collections, including creating folders, adding descriptions, and reordering requests.
-
Request and Response Visualization: Postman provides a user-friendly interface to view and analyze request and response data. It supports syntax highlighting for various data formats such as JSON, XML, and HTML, making it easier to understand and validate the data.
-
Environment and Variables: Postman allows you to define variables and environments. Variables enable you to store and reuse dynamic values across requests, making them flexible and easy to maintain. Environments provide sets of variables specific to different environments (e.g., development, staging, production).
-
Tests and Assertions: Postman supports writing test scripts using JavaScript for automated API testing. You can write assertions to validate response status codes, headers, response bodies, and more. Postman's testing framework allows you to assert and validate different aspects of API responses.
-
Pre-request Scripts: Postman enables you to execute scripts before sending API requests using pre-request scripts. These scripts can be used to dynamically generate values, manipulate data, or set variables based on specific conditions.
-
Collection Runner: The Collection Runner allows you to execute a series of requests in a collection. It enables you to perform data-driven testing by iterating over multiple sets of data or environments. You can configure iterations, delays, and data sources for more comprehensive testing.
-
Mock Servers: Postman allows you to create mock servers for simulating API responses without a live backend. Mock servers are useful during development, allowing frontend developers to work independently by providing simulated API responses.
-
Documentation Generation: Postman can automatically generate documentation for your APIs based on your requests and collections. It provides a simple way to share API specifications and details with stakeholders.
-
Collaboration and Teamwork: Postman offers collaboration features such as sharing collections, collaborating on requests, and commenting on specific requests or collections. It also supports version control integration to manage changes and updates effectively.
-
Integration and Automation: Postman integrates with various tools and services, including version control systems (e.g., Git), CI/CD platforms (e.g., Jenkins), and API management solutions. It provides options for integrating with these tools to automate API testing and deployment processes.