A complete API test automation project built using Postman and Newman, covering the full lifecycle of the Simple Books API. This project demonstrates real-world QA Engineering skills including request chaining, environment variables, automated token management, negative testing, and CI/CD-ready test execution.
- Project Overview
- Tools & Technologies
- API Under Test
- Test Collection Structure
- Test Scenarios
- Key Features
- CI/CD Pipeline
- Environment Setup
- How to Run Tests
- Test Results
- Lessons Learned
This project tests the Simple Books API — a RESTful API that allows users to:
- Check API availability
- Browse and retrieve books
- Submit, manage, and delete orders
The goal was to automate the full API workflow from authentication to order management, covering positive and negative test scenarios, verifying that all endpoints return correct responses and that data flows correctly between requests.
| Tool | Version | Purpose |
|---|---|---|
| Postman | Latest | Building and managing API requests |
| Newman | 6.2.2 | Running collections from command line |
| newman-reporter-htmlextra | Latest | Generating HTML test reports |
| JavaScript | ES6 | Writing test scripts and assertions |
| Node.js / npm | Latest | Installing Newman and plugins |
| PowerShell | Windows | Running Newman CLI commands |
| Jenkins | 2.541.2 | CI/CD pipeline automation |
| Git | 2.53.0 | Version control and GitHub integration |
Base URL: https://simple-books-api.click
| Endpoint | Method | Description | Auth Required |
|---|---|---|---|
/api-clients/ |
POST | Register and get access token | ❌ No |
/status |
GET | Check API health status | ❌ No |
/books |
GET | Get list of all books | ❌ No |
/books/:id |
GET | Get a single book by ID | ❌ No |
/orders |
POST | Submit a new order | ✅ Yes |
/orders |
GET | Get all orders | ✅ Yes |
/orders/:id |
GET | Get a single order | ✅ Yes |
/orders/:id |
PATCH | Update an order | ✅ Yes |
/orders/:id |
DELETE | Delete an order | ✅ Yes |
Books API Collection (14 Requests)
│
├── 📬 Create Token → POST /api-clients/
│ ├── Pre-request: Auto-generates random email
│ └── Post-response: Auto-saves access_token
│
├── 📗 Get Book Status → GET /status
├── 📗 List of Books → GET /books
├── 📗 Get Single Book → GET /books/1
├── ❌ Get Single Book - Invalid ID → GET /books/99999 (Negative Test)
│
├── 📮 Submit Order → POST /orders
│ └── Post-response: Auto-saves orderId
│
├── ❌ Submit Order - Invalid Book ID → POST /orders (Negative Test)
├── 📗 Get All Orders → GET /orders
├── 📗 Get Single Order → GET /orders/{{orderId}}
├── ❌ Get Single Order - Invalid ID → GET /orders/invalid-id (Negative Test)
├── 📝 Update Order → PATCH /orders/{{orderId}}
├── 🔍 Verify Order Updated → GET /orders/{{orderId}}
├── 🗑️ Delete Order → DELETE /orders/{{orderId}}
└── ❌ Verify Order Deleted → GET /orders/{{orderId}} (Negative Test)
Every request validates the correct HTTP status code:
201 Created → Create Token, Submit Order
200 OK → Get Status, List Books, Get Single Book, Get Orders
204 No Content → Update Order, Delete Order
400 Bad Request → Submit Order with invalid book ID
404 Not Found → Invalid book ID, Invalid order ID, Deleted order
Every request checks performance:
pm.test("Response time is under 2000ms", () => {
pm.expect(pm.response.responseTime).to.be.below(2000);
});Checks that all required fields exist and are the correct data type:
pm.test("Each book has required fields", () => {
response.forEach((book) => {
pm.expect(book).to.have.property("id");
pm.expect(book).to.have.property("name");
pm.expect(book).to.have.property("type");
pm.expect(book).to.have.property("available");
});
});Validates the full order creation flow:
pm.test("Order created is true", () => {
pm.expect(response.created).to.be.true;
});
pm.test("Response contains orderId", () => {
pm.expect(response.orderId).to.not.be.empty;
});Confirms retrieved data matches what was created:
pm.test("Order ID matches saved ID", () => {
pm.expect(response.id).to.eql(pm.environment.get("orderId"));
});
pm.test("Response contains customerName", () => {
pm.expect(response.customerName).to.eql("John Doe");
});Confirms update was applied correctly:
pm.test("Customer name was updated correctly", () => {
pm.expect(response.customerName).to.eql("Lola Updated");
});
pm.test("Book ID remains unchanged after update", () => {
pm.expect(response.bookId).to.eql(1);
});Confirms deleted order returns 404:
pm.test("Status is 404 Not Found after deletion", () => {
pm.response.to.have.status(404);
});
pm.test("Error message confirms order not found", () => {
pm.expect(response.error).to.not.be.empty;
});Three dedicated negative test scenarios:
GET /books/99999→ expects 404POST /orderswith bookId 99999 → expects 400GET /orders/invalid-order-id-99999→ expects 404GET /orders/{{orderId}}after deletion → expects 404
Pre-request script generates a unique email on every run:
const randomEmail = "lolatest" + Date.now() + "@gmail.com";
pm.environment.set("clientEmail", randomEmail);Post-response script saves the token automatically:
if (pm.response.code === 201) {
pm.environment.set("access_token", response.accessToken);
}Order ID flows automatically from Submit Order to all subsequent requests:
if (pm.response.code === 201) {
pm.environment.set("orderId", response.orderId);
}Every run is fully automated:
Run Newman → fresh email generated → token saved →
order created → orderId saved → all requests execute →
HTML report generated
CREATE → POST /orders (Submit Order)
READ → GET /orders (Get All Orders)
READ → GET /orders/:id (Get Single Order)
UPDATE → PATCH /orders/:id (Update Order)
DELETE → DELETE /orders/:id (Delete Order)
This project is integrated with Jenkins to run tests automatically.
Code pushed to GitHub
↓
Jenkins pulls latest collection from GitHub
↓
Newman runs all 14 requests and 61 assertions
↓
Results visible in Jenkins console output
↓
Build marked as passed ✅ or failed ❌
Job Name → Books-API-Tests
Job Type → Freestyle Project
Source Control → Git (GitHub repository)
Branch → */main
Build Trigger → GitHub hook trigger for GITScm polling
Build Step → Execute Windows batch command
C:\Users\LENOVO\AppData\Roaming\npm\newman.cmd run BooksAPI.json -e QA_Environment.json| Build | Status | Duration |
|---|---|---|
| #1 | ❌ Failed | 5.7s (newman path issue — fixed) |
| #2 | ✅ Passed | 9.8s |
main branch → stable, production-ready tests
feature/update-readme → new feature branch
↓
Pull Request opened on GitHub
↓
Reviewed and merged into main
↓
Feature branch deleted
npm install -g newmannpm install -g newman-reporter-htmlextranewman --version
# Expected output: 6.2.2Download BooksAPI.json and QA_Environment.json to your Desktop.
Note: No manual configuration needed. The
access_token,orderId, andclientEmailvariables are all populated automatically when the collection runs.
newman run "C:\Users\LENOVO\Desktop\BooksAPI.json" -e "C:\Users\LENOVO\Desktop\QA_Environment.json"newman run "C:\Users\LENOVO\Desktop\BooksAPI.json" -e "C:\Users\LENOVO\Desktop\QA_Environment.json" -r htmlextra --reporter-htmlextra-export "C:\Users\LENOVO\Desktop\TestReport.html"Start-Process "C:\Users\LENOVO\Desktop\TestReport.html"Note: A new unique email is automatically generated on every run — no manual changes needed.
| Metric | Result |
|---|---|
| Total Requests | 14 |
| Passed Requests | ✅ 14 |
| Failed Requests | ❌ 0 |
| Total Assertions | 61 |
| Passed Assertions | ✅ 61 |
| Failed Assertions | ❌ 0 |
| Total Duration | 5.9s |
| Average Response Time | 342ms |
| Min Response Time | 156ms |
| Max Response Time | 807ms |
| Request | Method | Status | Assertions |
|---|---|---|---|
| Create Token | POST | 201 Created ✅ | 4 |
| Get Book Status | GET | 200 OK ✅ | 4 |
| List of Books | GET | 200 OK ✅ | 7 |
| Get Single Book | GET | 200 OK ✅ | 8 |
| Get Single Book - Invalid ID | GET | 404 Not Found ✅ | 4 |
| Submit Order | POST | 201 Created ✅ | 5 |
| Submit Order - Invalid Book ID | POST | 400 Bad Request ✅ | 3 |
| Get All Orders | GET | 200 OK ✅ | 5 |
| Get Single Order | GET | 200 OK ✅ | 6 |
| Get Single Order - Invalid ID | GET | 404 Not Found ✅ | 3 |
| Update Order | PATCH | 204 No Content ✅ | 3 |
| Verify Order Updated | GET | 200 OK ✅ | 3 |
| Delete Order | DELETE | 204 No Content ✅ | 3 |
| Verify Order Deleted | GET | 404 Not Found ✅ | 3 |
| TOTAL | 61 ✅ |
1. CloudFront Stripping Authorization Headers
The .glitch.me domain was fronted by AWS CloudFront which stripped Authorization headers before they reached the API. Resolved by switching to the correct domain simple-books-api.click.
2. Environment Variables Not Resolving in Newman
Early runs failed because {{baseUrl}} had no protocol definition in the exported JSON. Fixed by using hardcoded full HTTPS URLs with proper protocol definitions.
3. Duplicate Authorization Headers During debugging, multiple Authorization headers accumulated causing conflicts. Resolved by cleaning the collection JSON directly and using only the Bearer Token auth type.
4. PowerShell Execution Policy Initial Newman installation was blocked by Windows PowerShell execution policy. Fixed using:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser5. Email Already Registered Error The API only allows one registration per email. Solved by implementing a pre-request script that generates a unique timestamped email on every run.
- API test design and execution
- Positive and negative test scenario design
- Request chaining and data passing between requests
- Environment and variable management
- Dynamic test data generation
- CLI test execution with Newman
- Debugging real API authentication issues
- Reading and interpreting HTTP status codes
- Professional test reporting with HTMLExtra
- GitHub project documentation
- Jenkins CI/CD pipeline setup and configuration
- Git CLI — branching, pull requests and merging
- Debugging Jenkins build failures from console output
Ololade Adekunle QA Engineer
- Tools: Postman, Newman, JavaScript, Git, Jenkins
- GitHub: lolahgrace
This project is open source and available for learning and reference purposes.