This it is part of assignment submitted to Deakin University, School of IT, Unit SIT331 - Full Stack Development Secure Backend Services. By s222575621.
IntroductionBackend development requires rigorous testing to ensure the robustness, functionality, and performance of APIs. Automated testing using tools like Postman is essential to streamline the testing process and catch defects early. This teaching case will guide learners through setting up and executing comprehensive API tests using Postman, emphasizing the importance of thorough testing in backend development.
ObjectiveThe objective is to understand and implement automated testing for backend APIs using Postman. Learners will create and execute tests for various API endpoints, ensuring they meet the expected functionality and performance standards. The process involves setting up Postman, writing test scripts, and optionally using Newman for CLI-based test automation.
Case SummaryThis teaching case involves the following steps:
- Setting up Postman: Install and configure Postman for API testing.
- Creating API Tests: Write tests to validate HTTP response codes, response bodies, and specific data structures.
- Advanced Testing: Implement additional tests to ensure comprehensive coverage.
- Optional: Using Newman: Automate tests using Newman in a CI/CD pipeline.
- Install Postman: Download and install Postman from Postman's official website.
- Set Up Postman: Create an account and familiarize yourself with the Postman interface.
For this task, you will use the provided backend endpoints related to user management. Ensure your backend server is running and accessible.
Writing Automated TestsPostman provides a robust scripting environment to write tests in JavaScript. Here's a step-by-step guide to writing tests for various endpoints.
1. Get All UsersEndpoint:GET https://{{api-host}}:{{port}}/api/users
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});
pm.test("Response body is an array", function () {
pm.expect(pm.response.json()).to.be.an('array');
});Explanation:1. Status Code Validation:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});- Purpose: This test checks if the HTTP response status code is 200, indicating a successful request.
- Why It's Needed: Ensuring the correct status code verifies that the API endpoint is functioning correctly and the server successfully processed the request.
2.Response Body Validation:
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});- Purpose: This test ensures that the response body is not empty, null, or undefined.
- Why It's Needed: A non-empty response body is critical for verifying that the API is returning meaningful data. This is particularly important for endpoints that are expected to return a list or a collection of resources.
3.Data Structure Validation:
pm.test("Response body is an array", function () {
pm.expect(pm.response.json()).to.be.an('array');
});- Purpose: This test checks if the response body is an array.
- Why It's Needed: Validating the data structure ensures that the API's response conforms to the expected format, which is crucial for consistency and predictability when consuming the API.
Endpoint:GET https://{{api-host}}:{{port}}/api/users/{{userId}}
pm.environment.set("userId", "1"); // Example user IDTest Script:pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});
pm.test("Response body is an object", function () {
pm.expect(pm.response.json()).to.be.an('object');
});
pm.test("User object contains valid properties", function () {
var user = pm.response.json();
pm.expect(user).to.have.property('id');
pm.expect(user).to.have.property('email');
pm.expect(user).to.have.property('firstName');
pm.expect(user).to.have.property('lastName');
pm.expect(user).to.have.property('role');
});Explanation:1.Status Code Validation:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});- Purpose: Confirms that the request was successfully processed by the server.
- Why It's Needed: A 200 status code indicates that the request was handled properly and returned a successful response.
2.Response Body Validation:
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});- Purpose: Ensures the response body contains data.
- Why It's Needed: An empty response for a valid request indicates a potential issue with the endpoint logic or data retrieval process.
3.Data Structure Validation:
pm.test("Response body is an object", function () {
pm.expect(pm.response.json()).to.be.an('object');
});- Purpose: Checks that the response is a single object.
- Why It's Needed: This is crucial for endpoints that return a specific resource identified by an ID, ensuring the API returns the expected format.
4.Property Validation:
pm.test("User o: An empty response for a valid request indicates a potential issue with the endpoint logic or data retrieval processy('id');
pm.expect(user).to.have.property('email');
pm.expect(user).to.have.property('firstName');
pm.expect(user).to.have.property('lastName');
pm.expect(user).to.have.property('role');
});- Purpose: Verifies that the returned user object has all the expected properties.
- Why It's Needed: Ensures data integrity and completeness, confirming that the API returns a fully populated user object.
Endpoint:POST https://{{api-host}}:{{port}}/api/users
pm.environment.set("newUserEmail", "test@example.com");
pm.environment.set("newUserFirstName", "John");
pm.environment.set("newUserLastName", "Doe");
pm.environment.set("newUserPasswordHash", "password");
pm.environment.set("newUserDescription", "A test user");
pm.environment.set("newUserRole", "User");Test Script:pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});
pm.test("Response body contains the newly created user", function () {
var user = pm.response.json();
pm.expect(user).to.have.property('id');
pm.expect(user).to.have.property('email');
pm.expect(user).to.have.property('firstName');
pm.expect(user).to.have.property('lastName');
pm.expect(user).to.have.property('role');
});
pm.test("Test newly created user email", function () {
var user = pm.response.json();
var expectedEmail = pm.environment.get('newUserEmail');
pm.expect(user.email).to.equal(expectedEmail);
});
pm.test("Check a newly created resource URL in the Location HTTP header", function () {
pm.response.to.have.header("Location");
var user = pm.response.json();
var newResourceUrl = pm.environment.get('baseUrl') + '/api/users/' + user.id;
pm.expect(pm.response.headers.get('Location')).to.eql(newResourceUrl);
});Explanation:1.Status Code Validation:
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});- Purpose: Validates that the user was successfully created.
- Why It's Needed: A 201 status code indicates that the request has succeeded and a new resource has been created.
2.Response Body Validation:
pm.test("Response body is not empty", function () {
pm.response.to.have.jsonBody();
pm.response.to.not.be.null;
pm.response.to.not.be.undefined;
});- Purpose: Ensures that the response body contains data.
- Why It's Needed: Confirms that the API returns details of the newly created user, which is necessary for verification and further processing.
3.Property Validation:
pm.test("Response body contains the newly created user", function () {
var user = pm.response.json();
pm.expect(user).to.have.property('id');
pm.expect(user).to.have.property('email');
pm.expect(user).to.have.property('firstName');
pm.expect(user).to.have.property('lastName');
pm.expect(user).to.have.property('role');
});- Purpose: Checks that the response body includes all expected properties of the new user.
- Why It's Needed: Ensures that the created user object is complete and correctly formed.
4.Email Validation:
pm.test("Test newly created user email", function () {
var user = pm.response.json();
var expectedEmail = pm.environment.get('newUserEmail');
pm.expect(user.email).to.equal(expectedEmail);
});- Purpose: Verifies that the email of the newly created user matches the expected email.
- Why It's Needed: Confirms that the user was created with the correct data.
5.Resource URL Validation:
pm.test("Check a newly created resource URL in the Location HTTP header", function () {
pm.response.to.have.header("Location");
var user = pm.response.json();
var newResourceUrl = pm.environment.get('baseUrl') + '/api/users/' + user.id;
pm.expect(pm.response.headers.get('Location')).to.eql(newResourceUrl);
});- Purpose: Ensures that the Location header contains the correct URL for the newly created user resource.
- Why It's Needed: Validates that the API provides a proper reference to the new resource, which is useful for subsequent operations like retrieval or update.
Endpoint:PUT https://{{api-host}}:{{port}}/api/users/{{userId}}
pm.environment.set("userId", "1"); // Example user ID
pm.environment.set("newUserFirstName", "Jane");
pm.environment.set("newUserLastName", "Doe");
pm.environment.set("newUserPasswordHash", "newpassword");
pm.environment.set("newUserDescription", "Updated user description");
pm.environment.set("newUserRole", "Admin");Explanation:- Purpose: This script sets environment variables that will be used in the request body to update the user details.
- Why It's Needed: Setting environment variables ensures that the request body is dynamically populated with the values needed for the update. This approach is flexible and reusable for different test cases and environments.
pm.test("Status code is 204", function () {
pm.response.to.have.status(204);
});
// Verify the user was updated
const userId = pm.environment.get("userId");
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/api/users/" + userId,
method: 'GET',
header: {
'Authorization': pm.environment.get("authHeader")
}
}, function (err, res) {
var user = res.json();
pm.test("User data is updated", function () {
pm.expect(user).to.have.property("firstName", pm.environment.get("newUserFirstName"));
pm.expect(user).to.have.property("lastName", pm.environment.get("newUserLastName"));
pm.expect(user).to.have.property("role", pm.environment.get("newUserRole"));
});
});Explanation:1.Status Code Validation:
pm.test("Status code is 204", function () {
pm.response.to.have.status(204);
});- Purpose: Validates that the update operation was successful and the server responded with a
204 No Contentstatus code. - Why It's Needed: A
204status code indicates that the server successfully processed the request and is not returning any content. This is typical for update operations.
2.Verify User Update:
const userId = pm.environment.get("userId");
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/api/users/" + userId,
method: 'GET',
header: {
'Authorization': pm.environment.get("authHeader")
}
}, function (err, res) {
var user = res.json();
pm.test("User data is updated", function () {
pm.expect(user).to.have.property("firstName", pm.environment.get("newUserFirstName"));
pm.expect(user).to.have.property("lastName", pm.environment.get("newUserLastName"));
pm.expect(user).to.have.property("role", pm.environment.get("newUserRole"));
});
});- Purpose: Sends a
GETrequest to retrieve the updated user data and validate the changes. - Why It's Needed: Verifying the update ensures that the changes made in the
PUTrequest are correctly reflected in the user's data. This step is crucial for confirming the success of the update operation.
Endpoint:DELETE https://{{api-host}}:{{port}}/api/users/{{userId}}
pm.environment.set("userId", "1"); // Example user ID
pm.environment.set("authHeader", "Basic YOUR_BASE64_ENCODED_CREDENTIALS");Explanation:- Purpose: This script sets the environment variables for the user ID to be deleted and the authorization header needed for the request.
- Why It's Needed: Setting these variables ensures that the delete request is properly authenticated and targets the correct user.
pm.test("Status code is 204", function () {
pm.response.to.have.status(204);
});
// Verify the user was deleted
const userId = pm.environment.get("userId");
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/api/users/" + userId,
method: 'GET',
header: {
'Authorization': pm.environment.get("authHeader")
}
}, function (err, res) {
pm.test("User is deleted and returns 404 Not Found", function () {
pm.expect(res.code).to.equal(404);
});
});Explanation:1.Status Code Validation:
pm.test("Status code is 204", function () {
pm.response.to.have.status(204);
});- Purpose: Validates that the delete operation was successful and the server responded with a
204 No Contentstatus code. - Why It's Needed: A
204status code indicates that the server successfully processed the request and is not returning any content. This is typical for delete operations.
2.Verify User Deletion:
const userId = pm.environment.get("userId");
pm.sendRequest({
url: pm.environment.get("baseUrl") + "/api/users/" + userId,
method: 'GET',
header: {
'Authorization': pm.environment.get("authHeader")
}
}, function (err, res) {
pm.test("User is deleted and returns 404 Not Found", function () {
pm.expect(res.code).to.equal(404);
});
});- Purpose: Sends a
GETrequest to retrieve the deleted user data and validate that the user no longer exists. - Why It's Needed: Verifying the deletion ensures that the user has been successfully removed from the system. A
404 Not Foundstatus code confirms that the user no longer exists, validating the success of the delete operation.
Newman allows you to run Postman collections from the command line, making it ideal for integration into CI/CD pipelines.
Installation1.Install Node.js: Download and install Node.js from nodejs.org.
2.Install Newman: Run the following command in your terminal:
npm install -g newmanRunning Collections1.Export Postman Collection: Export your collection and environment variables as JSON files.
2.Run Newman: Use the following command to execute the collection:
newman run postman-request-collection.json -e postman-environment-variables.json3.Generate Reports: Generate HTML reports using Newman reporters:
newman run postman-request-collection.json -e postman-environment-variabDemonstration video:ConclusionThis teaching case provides a thorough guide to setting up and executing automated tests for backend APIs using Postman. By following the outlined steps, learners gain a solid understanding of API testing, ensuring that their backend services are robust, functional, and performant. The step-by-step instructions cover essential aspects such as setting up Postman, writing test scripts, and validating API responses, which are crucial for maintaining high-quality backend systems.
Moreover, the case emphasizes the importance of testing various HTTP methods (GET, POST, PUT, DELETE) and validating responses not only through status codes but also by checking the integrity and structure of the returned data. This comprehensive testing approach helps identify and rectify issues early in the development process, thereby reducing the likelihood of defects reaching the production stage.
For advanced learners, integrating Newman into their CI/CD pipelines offers an opportunity to automate the testing process, facilitating continuous testing and deployment. This integration ensures that API tests are consistently executed in different environments, promoting a more reliable and seamless development lifecycle.
By rigorously implementing and testing the described methods, students build a deeper understanding of backend testing practices. This preparation equips them to tackle more complex development and testing challenges, fostering a mindset of thorough validation and quality assurance in software development. This teaching case, therefore, not only imparts technical skills but also instills best practices for maintaining robust and high-quality backend services.
This it is part of assignment submitted to Deakin University, School of IT, Unit SIT331 - Full Stack Development Secure Backend Services. By s222575621.





Comments