Article 2: Advanced Topics and Practice
How to Prepare for a QA Engineer Interview: Part 2 — Advanced Topics and Practice
From theory to practice: API testing, automation, SQL, and real cases from interviews

In Part 1, we covered theoretical testing fundamentals. Now let’s move to practice — the skills that truly distinguish junior QA from middle and senior specialists.
In my experience, 80% of candidates fail interviews not on theory, but on practical tasks. They know what regression testing is but can’t write a good test case. They understand testing principles but get lost when asked to test a real function.
This article will fix that. We’ll cover:
- How to write quality test cases
- API testing with examples
- SQL for QA engineers
- Automation basics
- 15+ practical tasks from interviews
- Real cases and how to solve them
📋 Table of Contents
- The Art of Writing Test Cases
- API Testing: From Basics to Practice
- SQL for QA: What You Need to Know
- Introduction to Test Automation
- 15 Practical Tasks from Interviews
- How to Test Popular Features
- Working with Tools
- Advice from Senior QA
📝 The Art of Writing Test Cases
Anatomy of a Perfect Test Case
Bad Test Case:
ID: TC001
Title: Test login
Steps: Login to system
Expected: User logged in
Why it’s bad?
- No preconditions
- Steps not specific
- No test data
- Cannot reproduce
- No result information
Good Test Case:
Test Case ID: TC_LOGIN_001
Title: Verify successful login with valid credentials
Preconditions:
- User is registered with email: test@example.com
- Password: Test@123
- User is on login page
- Browser: Chrome (latest)
Test Steps:
1. Navigate to https://example.com/login
2. Enter email: test@example.com in "Email" field
3. Enter password: Test@123 in "Password" field
4. Click "Login" button
Expected Results:
- User is redirected to dashboard page (URL: /dashboard)
- Welcome message "Hello, Test User" is displayed
- User avatar appears in top-right corner
- Session cookie is created
Actual Results: [To be filled during execution]
Status: [Pass/Fail]
Executed by: [Name]
Execution Date: [Date]
Test Data:
- Valid Email: test@example.com
- Valid Password: Test@123
Environment:
- OS: Windows 11
- Browser: Chrome 120.0
- Test Environment: Staging
Why it’s good? ✅ Can reproduce without additional questions ✅ Clear, measurable results ✅ Complete environment information ✅ Specific test data
Types of Test Cases with Examples
1. Positive Test Cases
Goal: Verify system works with valid data
Example: User Registration
TC_REG_001: Registration with valid data
- Name: John Doe (2-50 characters)
- Email: john.doe@example.com (valid format)
- Password: SecurePass123! (8+ characters, letters, digits, special chars)
- Confirm Password: SecurePass123! (matches)
Expected:
✅ Registration successful
✅ Confirmation email sent
✅ User redirected to verification page
2. Negative Test Cases
Goal: Verify handling of incorrect data
Example: Registration with invalid data
TC_REG_002: Registration with short password
- Password: 123
Expected:
❌ Registration FAILED
✅ Message: "Password must contain at least 8 characters"
✅ Password field highlighted in red
✅ User stays on registration page
TC_REG_003: Registration with existing email
- Email: existing@example.com
Expected:
❌ Registration FAILED
✅ Message: "User with this email already exists"
TC_REG_004: Registration with invalid email
- Email: notanemail
Expected:
❌ Registration FAILED
✅ Message: "Enter valid email address"
TC_REG_005: SQL Injection attempt
- Name: Robert'); DROP TABLE users;--
Expected:
❌ Registration FAILED
✅ Special characters escaped
✅ SQL injection blocked
3. Boundary Test Cases
Example: “Age” field (18-65 years)
TC_AGE_001: Age = 17 (min-1)
Expected: ❌ "Minimum age is 18"
TC_AGE_002: Age = 18 (min)
Expected: ✅ Accepted
TC_AGE_003: Age = 19 (min+1)
Expected: ✅ Accepted
TC_AGE_004: Age = 64 (max-1)
Expected: ✅ Accepted
TC_AGE_005: Age = 65 (max)
Expected: ✅ Accepted
TC_AGE_006: Age = 66 (max+1)
Expected: ❌ "Maximum age is 65"
TC_AGE_007: Age = 0
Expected: ❌ "Enter valid age"
TC_AGE_008: Age = -5
Expected: ❌ "Age cannot be negative"
TC_AGE_009: Age = "abc"
Expected: ❌ "Age must be a number"
Practical Interview Assignment
Task: “Write test cases for ‘Forgot Password?’ function”
My Solution:
Positive Scenarios:
TC_FORGOT_001: Reset password with registered email
Preconditions: User with test@example.com is registered
Steps:
1. Click "Forgot Password?"
2. Enter test@example.com
3. Click "Send"
Expected:
- Message: "Email sent to test@example.com"
- Email received within 2 minutes
- Link in email is valid
- Redirect to new password creation page
- New password successfully set
Negative Scenarios:
TC_FORGOT_002: Unregistered email
Steps: Enter notexist@example.com
Expected:
- Message: "User not found"
OR (more secure)
- Message: "If email exists, email will be sent"
TC_FORGOT_003: Empty email field
Expected: "Enter email address"
TC_FORGOT_004: Invalid email format
Input: notanemail
Expected: "Enter valid email"
TC_FORGOT_005: Multiple requests (rate limiting)
Steps: Send 10 requests in 1 minute
Expected:
- After 5 requests: "Too many attempts. Try again in 15 minutes"
- IP/email blocking
Additional Checks:
TC_FORGOT_006: Link expiration
Steps: Use link after 25 hours (if limit is 24h)
Expected: "Link expired. Request a new one"
TC_FORGOT_007: Using link twice
Steps:
1. Follow link, change password
2. Try to use the same link again
Expected: "Link already used"
TC_FORGOT_008: SQL Injection in email field
Input: '; DROP TABLE users; --
Expected: Request safely handled
TC_FORGOT_009: XSS attempt
Input: <script>alert('XSS')</script>@test.com
Expected: Special characters escaped
🔌 API Testing: From Basics to Practice
What is API?
API (Application Programming Interface) — interface through which applications communicate with each other.
Real-life analogy: You (client) → Waiter (API) → Kitchen (server)
You don’t go to the kitchen directly. The waiter takes your order (request), passes it to the kitchen, and brings you food (response).
REST API Basics
Main HTTP Methods:
| Method | Purpose | Example |
|---|---|---|
| GET | Retrieve data | Get list of users |
| POST | Create new resource | Create new user |
| PUT | Update entire resource | Update all user data |
| PATCH | Update part of resource | Update only user email |
| DELETE | Delete resource | Delete user |
Response Codes (Status Codes):
2xx — Success:
- 200 OK: Request successful
- 201 Created: Resource created
- 204 No Content: Successful but no data to return
4xx — Client Errors:
- 400 Bad Request: Invalid request
- 401 Unauthorized: Not authorized
- 403 Forbidden: Access denied
- 404 Not Found: Resource not found
- 422 Unprocessable Entity: Validation failed
5xx — Server Errors:
- 500 Internal Server Error: Server error
- 502 Bad Gateway: Proxy issues
- 503 Service Unavailable: Service unavailable
Practical API Testing
Scenario: Testing User API
Endpoint: https://api.example.com/users
1. GET Request — Get list of users
Request:
GET /api/users HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
What we test:
✅ Status Code:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
✅ Response Time:
pm.test("Response time is less than 500ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
✅ Response Structure:
pm.test("Response has correct structure", function () {
var jsonData = pm.response.json();
pm.expect(jsonData).to.have.property('data');
pm.expect(jsonData.data).to.be.an('array');
pm.expect(jsonData.data[0]).to.have.property('id');
pm.expect(jsonData.data[0]).to.have.property('name');
pm.expect(jsonData.data[0]).to.have.property('email');
});
✅ Data Types:
pm.test("Data types are correct", function () {
var user = pm.response.json().data[0];
pm.expect(user.id).to.be.a('number');
pm.expect(user.name).to.be.a('string');
pm.expect(user.email).to.be.a('string');
pm.expect(user.active).to.be.a('boolean');
});
2. POST Request — Create user
Request:
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "John Doe",
"email": "john.doe@example.com",
"password": "SecurePass123!"
}
Test Cases:
// TC_API_001: Create with valid data
pm.test("User created successfully", function () {
pm.response.to.have.status(201);
var jsonData = pm.response.json();
pm.expect(jsonData.message).to.eql("User created successfully");
pm.expect(jsonData.data.name).to.eql("John Doe");
pm.expect(jsonData.data.email).to.eql("john.doe@example.com");
// Save ID for subsequent tests
pm.environment.set("userId", jsonData.data.id);
});
// TC_API_002: Create with existing email
pm.test("Duplicate email returns 422", function () {
pm.response.to.have.status(422);
var jsonData = pm.response.json();
pm.expect(jsonData.error).to.include("email already exists");
});
// TC_API_003: Create without required field
pm.test("Missing required field returns 400", function () {
pm.response.to.have.status(400);
var jsonData = pm.response.json();
pm.expect(jsonData.errors).to.have.property("email");
});
// TC_API_004: Invalid email format
pm.test("Invalid email format returns 422", function () {
pm.response.to.have.status(422);
var jsonData = pm.response.json();
pm.expect(jsonData.errors.email).to.include("valid email");
});
API Security Testing
Important Security Checks:
1. Authentication
// TC_SEC_001: Request without token
pm.test("Request without token returns 401", function () {
pm.response.to.have.status(401);
});
// TC_SEC_002: Request with invalid token
pm.test("Request with invalid token returns 401", function () {
pm.response.to.have.status(401);
var jsonData = pm.response.json();
pm.expect(jsonData.error).to.include("Invalid token");
});
// TC_SEC_003: Expired token
pm.test("Expired token returns 401", function () {
pm.response.to.have.status(401);
var jsonData = pm.response.json();
pm.expect(jsonData.error).to.include("Token expired");
});
2. Authorization
// TC_SEC_004: Regular user cannot delete another user
pm.test("Non-admin cannot delete user", function () {
pm.response.to.have.status(403);
});
🗄️ SQL for QA Engineers
Why QA Needs SQL?
- Data Validation — verify API correctly saved data in DB
- Test Data Preparation — create test records
- Logic Verification — ensure business logic correctness
- Debugging — understand bug cause
- Data Cleanup — clean test data
Basic SQL Commands for QA
1. SELECT — Retrieving Data
Basic SELECT:
-- Get all users
SELECT * FROM users;
-- Get specific columns
SELECT id, name, email FROM users;
-- With condition
SELECT * FROM users WHERE email = 'test@example.com';
-- With sorting
SELECT * FROM users ORDER BY created_at DESC;
-- With limit
SELECT * FROM users LIMIT 10;
Practical Example:
-- Verify user created after API call
SELECT id, name, email, created_at
FROM users
WHERE email = 'john.doe@example.com'
AND created_at > NOW() - INTERVAL '5 minutes';
2. WHERE — Filtering
-- Equals
SELECT * FROM users WHERE status = 'active';
-- Not equals
SELECT * FROM users WHERE status != 'deleted';
-- Greater/less than
SELECT * FROM orders WHERE total_amount > 100;
-- LIKE (pattern search)
SELECT * FROM users WHERE email LIKE '%@gmail.com';
-- IN (one of values)
SELECT * FROM orders WHERE status IN ('pending', 'processing');
-- BETWEEN (range)
SELECT * FROM orders WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31';
-- IS NULL / IS NOT NULL
SELECT * FROM users WHERE deleted_at IS NULL;
3. JOIN — Joining Tables
INNER JOIN:
-- Get orders with user information
SELECT
orders.id,
orders.total_amount,
users.name,
users.email
FROM orders
INNER JOIN users ON orders.user_id = users.id;
LEFT JOIN:
-- Get all users and their orders (even if no orders)
SELECT
users.name,
COUNT(orders.id) as order_count
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.id, users.name;
Practical Case:
-- Find users without orders (potential issue?)
SELECT users.id, users.name, users.email
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.id IS NULL;
4. GROUP BY and Aggregate Functions
-- COUNT - counting
SELECT status, COUNT(*) as count
FROM orders
GROUP BY status;
-- SUM - sum
SELECT user_id, SUM(total_amount) as total_spent
FROM orders
GROUP BY user_id;
-- AVG - average
SELECT AVG(total_amount) as average_order_value
FROM orders;
-- MIN and MAX
SELECT
MIN(total_amount) as smallest_order,
MAX(total_amount) as largest_order
FROM orders;
Practical SQL Queries for Testing
Scenario 1: Validation After Transaction
Task: After API call creating order, verify:
- Order created in DB
- User balance decreased
- Status correct
-- 1. Verify order created
SELECT * FROM orders
WHERE user_id = 123
AND created_at > NOW() - INTERVAL '1 minute'
ORDER BY created_at DESC
LIMIT 1;
-- 2. Verify balance changed
SELECT
u.id,
u.balance as current_balance,
(SELECT balance FROM user_balance_history
WHERE user_id = u.id
ORDER BY created_at DESC
LIMIT 1 OFFSET 1) as previous_balance,
u.balance - (SELECT balance FROM user_balance_history
WHERE user_id = u.id
ORDER BY created_at DESC
LIMIT 1 OFFSET 1) as balance_change
FROM users u
WHERE u.id = 123;
-- 3. Verify status
SELECT status FROM orders WHERE id = 456;
-- Expected: 'pending' or 'paid'
Scenario 2: Data Integrity Check
-- Find orphaned orders (orders without user)
SELECT orders.*
FROM orders
LEFT JOIN users ON orders.user_id = users.id
WHERE users.id IS NULL;
-- Find users with negative balance
SELECT id, name, balance
FROM users
WHERE balance < 0;
-- Check for duplicate emails
SELECT email, COUNT(*) as count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
Scenario 3: Business Logic Check
-- Verify order total = items sum + shipping
SELECT
o.id,
o.total_amount,
SUM(oi.price * oi.quantity) as items_total,
o.shipping_cost,
SUM(oi.price * oi.quantity) + o.shipping_cost as calculated_total,
o.total_amount - (SUM(oi.price * oi.quantity) + o.shipping_cost) as difference
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id
HAVING ABS(o.total_amount - (SUM(oi.price * oi.quantity) + o.shipping_cost)) > 0.01;
-- If difference != 0, there's a calculation error!
🤖 Introduction to Test Automation
When Do You Need Automation?
Automate if: ✅ Tests repeat regularly (regression) ✅ Test takes long time manually ✅ Need to test on multiple configurations ✅ High risk of human error ✅ ROI justified (automation will pay off)
DON’T automate if: ❌ Test executes rarely ❌ UI changes frequently ❌ Visual assessment required ❌ Exploratory testing ❌ Usability testing
Automation Pyramid
/\
/ \ E2E Tests (10%)
/ \ - Slow
/ UI \ - Brittle
/--------\ - Expensive
/ \
/ Integration\ Integration Tests (30%)
/ Tests \ - Medium speed
/______________\ - API, services
\ /
\ / Unit Tests (60%)
\ Unit / - Fast
\ Tests / - Reliable
\ / - Cheap
\ /
\ /
\/
Ideal Distribution:
- 60% — Unit tests (developers)
- 30% — Integration/API tests (QA + Dev)
- 10% — E2E UI tests (QA)
💼 15 Practical Tasks from Interviews
Task 1: Testing Search Field
Question: “How would you test the search field on Amazon?”
Answer:
Functional Testing:
- Search with valid query
- Search with partial match
- Search with typos (autocorrect?)
- Search with empty query
- Search with special characters
- Search with very long text (1000+ characters)
- Search in different languages
- Case sensitivity
- Filters and sorting results
- Results pagination
Non-Functional: 11. Performance: response time < 2 sec 12. Search 1000 products simultaneously 13. Autocomplete (suggestions) 14. Search history 15. Caching
Security: 16. SQL Injection: ' OR '1'='1 17. XSS: <script>alert('xss')</script>
Usability: 18. Clear search button 19. Highlight search query in results 20. “No results found” — useful suggestions
Task 2: Testing a Button
Question: “How would you test a ‘Submit’ button?”
Answer:
Visual:
- Button displays correctly
- Correct text
- Correct color and style
- Correct size
- Hover effect works
- Disabled state displays
Functional: 7. Mouse click works 8. Keyboard Enter works 9. Touch on mobile works 10. Performs correct action 11. Disabled button not clickable 12. Loading state during processing
Accessibility: 13. Tab navigation available 14. Screen reader reads button 15. ARIA attributes present
Performance: 16. Double-click prevention 17. Debouncing when necessary
Cross-browser: 18. Chrome, Firefox, Safari, Edge 19. Mobile browsers
Task 3: Testing Shopping Cart
Question: “Design tests for shopping cart”
Answer:
Adding Items:
- Add 1 item
- Add multiple items
- Add same item twice (quantity increases)
- Add out of stock item
- Add more than available in stock
- Add item with discount
- Add item with variations (size, color)
Updating Quantity: 8. Increase quantity 9. Decrease quantity 10. Set quantity = 0 (item removed?) 11. Enter negative number 12. Enter non-numeric value 13. Exceed available quantity
Removal: 14. Remove one item 15. Remove all items 16. Clear cart
Calculations: 17. Subtotal correct 18. Apply promo code 19. Tax calculation 20. Shipping calculation 21. Total = subtotal + tax + shipping - discount
Persistence: 22. Cart saves after logout/login 23. Cart syncs between devices 24. Cart saved in cookies/localStorage
Edge Cases: 25. Maximum items in cart 26. Item removed from catalog (but in cart) 27. Item price changed 28. Stock quantity decreased
Task 4: Testing Login/Logout Flow
Test Cases:
Login - Positive:
- Valid email + password
- “Remember me” works
- Redirect to intended page after login
Login - Negative: 4. Wrong password → error 5. Non-existent email → error 6. Empty fields → error 7. SQL injection attempt 8. After 5 failed attempts → lockout 9. Expired session → requires re-login
Logout: 10. Logout clears session 11. Back button after logout → requires login 12. Tokens invalidated 13. “Logout from all devices” works
Task 5: Testing Payment Form
Critical Checks:
Card Data:
- Valid card number (Luhn algorithm)
- Invalid card number
- Expired card
- Wrong CVV
- Test cards work (staging)
Security: 6. Card number masked (**** **** **** 1234) 7. CVV not saved 8. HTTPS connection 9. PCI DSS compliance
Payment Process: 10. Successful payment 11. Declined payment 12. Timeout 13. Insufficient funds 14. 3D Secure verification
Integration: 15. Webhook received 16. Confirmation email sent 17. Order updated in DB 18. Invoice generated
🎯 How to Test Popular Features
Testing Calculator
Basic Operations:
+ : Addition
- : Subtraction
× : Multiplication
÷ : Division
Test Cases:
Positive:
- 2 + 2 = 4
- 10 - 5 = 5
- 3 × 4 = 12
- 20 ÷ 4 = 5
- 0 + 5 = 5
- 10 - 0 = 10
- Negative numbers: -5 + 3 = -2
- Decimal numbers: 1.5 + 2.5 = 4
- Large numbers: 999999 + 1 = 1000000
Boundary: 10. Division by zero → Error 11. Maximum number 12. Minimum number 13. Many decimal places
Order of Operations: 14. 2 + 3 × 4 = 14 (not 20) 15. (2 + 3) × 4 = 20 16. Nested parentheses
UI: 17. CE (Clear Entry) vs C (Clear All) 18. Backspace deletes last digit 19. Comma vs period (localization)
🛠️ Working with Tools
JIRA Best Practices
Well-Written Bug:
Summary:
[Login] Unable to login with valid credentials on Chrome
Environment:
OS: Windows 11
Browser: Chrome 120.0.6099
URL: https://staging.example.com/login
Account: test@example.com
Steps to Reproduce:
1. Navigate to https://staging.example.com/login
2. Enter email: test@example.com
3. Enter password: Test@123
4. Click "Login" button
Expected Result:
- User is logged in
- Redirected to /dashboard
Actual Result:
- Login button shows loading spinner indefinitely
- No error message displayed
- User remains on login page
Additional Information:
- Console error: "TypeError: Cannot read property 'token' of undefined"
- Network tab shows 500 Internal Server Error for POST /api/auth/login
- Issue reproducible 100% of the time
- Works fine on Firefox and Safari
Attachments:
- screenshot.png
- console_log.txt
- network_har_file.har
Severity: High
Priority: High
Affected Version: 2.1.0
💡 Advice from Senior QA
1. Think Like a User
Don’t just follow test cases. Ask yourself:
- How will a real user use this?
- What unexpected actions might they take?
- What could go wrong?
2. Document Everything
- Clear reproduction steps
- Screenshots/video
- Logs
- Environment details
Bad: “Bug in login” Good: [Example above in JIRA section]
3. Automate Smartly
Don’t automate everything. Start with:
- Smoke tests
- Regression for stable features
- API tests (high ROI)
4. Communication is Key
- Clearly explain bugs
- Be proactive
- Participate in planning
- Share feedback
5. Never Stop Learning
- New tools
- New methodologies
- New technologies
🎓 Final Checklist Before Interview
✅ Practice:
- Wrote 50+ test cases
- Found real bugs on demo sites
- Practiced API testing in Postman
- Wrote 10+ SQL queries
- Created simple automation script
- Solved all 15 practical tasks
✅ Theory:
- Can explain everything from Part 1
- Understand difference between all testing types
- Know when to use each test design technique
- Confidently explain API testing
- Understand SQL basics
✅ Soft Skills:
- Prepared 5+ STAR stories
- Ready to explain testing approach
- Can talk about past projects
- Prepared questions for interviewer
🚀 Conclusion
Preparing for QA interview isn’t just memorization. It’s developing QA mindset:
- Critical thinking
- Attention to detail
- Analytical skills
- Communication
Remember three main tips:
-
Practice > Theory — write test cases, find bugs, test real applications
-
Understanding > Memorization — important not to memorize definitions but understand concepts
-
Curiosity > Knowledge — show in interview that you love learning and growing
Good luck on your interview! 🍀
If this article helped you, give it a 👏 and share with friends!
Have questions? Write in comments — I’ll answer all!