API Testing with JSON: Tools and Techniques
Testing REST APIs is crucial for reliable applications. Since most modern APIs use JSON for data exchange, mastering JSON validation and testing techniques is essential. This guide covers everything from basic validation to advanced testing strategies.
Why JSON API Testing Matters
- Data Integrity - Ensure correct data format and structure
- Error Handling - Validate error responses and status codes
- Performance - Check response times and payload sizes
- Security - Test for injection attacks and data leaks
Essential Testing Tools
1. curl - Command Line Testing
curl is perfect for quick API tests and automation:
# GET request
curl -X GET "https://api.example.com/users" \
-H "Accept: application/json"
# POST request with JSON data
curl -X POST "https://api.example.com/users" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com"
}'
# Pretty print JSON response
curl -s "https://api.example.com/users" | python -m json.tool
# Save response to file
curl "https://api.example.com/users" -o response.json
2. Postman - GUI Testing
Postman provides a user-friendly interface for API testing:
- Collections - Organize related API tests
- Environment Variables - Manage different environments
- Pre-request Scripts - Set up test data
- Tests - Validate responses automatically
Postman Test Script Example:
// Test status code
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
// Test JSON structure
pm.test("Response has required fields", function () {
const jsonData = pm.response.json();
pm.expect(jsonData).to.have.property('id');
pm.expect(jsonData).to.have.property('name');
pm.expect(jsonData).to.have.property('email');
});
// Test data types
pm.test("ID is a number", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.id).to.be.a('number');
});
3. Python requests - Programmatic Testing
Python requests library is excellent for automated testing:
import requests
import json
def test_api_endpoint():
# Test GET request
response = requests.get('https://api.example.com/users/1')
# Basic assertions
assert response.status_code == 200
assert response.headers['content-type'] == 'application/json'
# Parse JSON response
data = response.json()
# Validate JSON structure
required_fields = ['id', 'name', 'email']
for field in required_fields:
assert field in data, f"Missing required field: {field}"
# Validate data types
assert isinstance(data['id'], int)
assert isinstance(data['name'], str)
assert isinstance(data['email'], str)
return data
# Test POST request
def test_create_user():
user_data = {
"name": "Alice Smith",
"email": "alice@example.com"
}
response = requests.post(
'https://api.example.com/users',
json=user_data,
headers={'Content-Type': 'application/json'}
)
assert response.status_code == 201
created_user = response.json()
# Validate created user
assert created_user['name'] == user_data['name']
assert created_user['email'] == user_data['email']
assert 'id' in created_user
return created_user
JSON Validation Techniques
1. Schema Validation
Use JSON Schema to validate response structure:
import jsonschema
import requests
# Define expected schema
user_schema = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0},
"active": {"type": "boolean"}
},
"required": ["id", "name", "email"]
}
def validate_user_response(response_data):
try:
jsonschema.validate(response_data, user_schema)
print("✅ JSON schema validation passed")
return True
except jsonschema.ValidationError as e:
print(f"❌ Schema validation failed: {e.message}")
return False
# Test with API response
response = requests.get('https://api.example.com/users/1')
user_data = response.json()
validate_user_response(user_data)
2. Custom Validation Functions
def validate_api_response(response, expected_fields=None, expected_types=None):
"""
Comprehensive API response validation
"""
# Check status code
if not 200 <= response.status_code < 300:
raise AssertionError(f"Unexpected status code: {response.status_code}")
# Check content type
content_type = response.headers.get('content-type', '')
if 'application/json' not in content_type:
raise AssertionError(f"Expected JSON, got: {content_type}")
# Parse JSON
try:
data = response.json()
except ValueError as e:
raise AssertionError(f"Invalid JSON response: {e}")
# Validate fields
if expected_fields:
missing_fields = set(expected_fields) - set(data.keys())
if missing_fields:
raise AssertionError(f"Missing fields: {missing_fields}")
# Validate types
if expected_types:
for field, expected_type in expected_types.items():
if field in data and not isinstance(data[field], expected_type):
raise AssertionError(
f"Field '{field}' should be {expected_type.__name__}, "
f"got {type(data[field]).__name__}"
)
return data
# Usage example
response = requests.get('https://api.example.com/users/1')
user_data = validate_api_response(
response,
expected_fields=['id', 'name', 'email'],
expected_types={'id': int, 'name': str, 'email': str}
)
Testing Different Scenarios
1. Error Response Testing
def test_error_responses():
# Test 404 - Not Found
response = requests.get('https://api.example.com/users/99999')
assert response.status_code == 404
error_data = response.json()
assert 'error' in error_data
assert 'message' in error_data
# Test 400 - Bad Request
invalid_data = {"email": "invalid-email"}
response = requests.post(
'https://api.example.com/users',
json=invalid_data
)
assert response.status_code == 400
error_data = response.json()
assert 'validation_errors' in error_data
# Test authentication errors
def test_auth_errors():
# Test without token
response = requests.get('https://api.example.com/protected')
assert response.status_code == 401
# Test with invalid token
headers = {'Authorization': 'Bearer invalid-token'}
response = requests.get('https://api.example.com/protected', headers=headers)
assert response.status_code == 401
2. Performance Testing
import time
def test_response_time():
start_time = time.time()
response = requests.get('https://api.example.com/users')
end_time = time.time()
response_time = end_time - start_time
# Assert response time is under 2 seconds
assert response_time < 2.0, f"Response too slow: {response_time:.2f}s"
# Check response size
content_length = len(response.content)
assert content_length < 1024 * 1024, f"Response too large: {content_length} bytes"
Automated Testing with pytest
import pytest
import requests
class TestUserAPI:
base_url = "https://api.example.com"
def test_get_users(self):
response = requests.get(f"{self.base_url}/users")
assert response.status_code == 200
users = response.json()
assert isinstance(users, list)
if users: # If users exist
user = users[0]
assert 'id' in user
assert 'name' in user
assert 'email' in user
def test_create_user(self):
user_data = {
"name": "Test User",
"email": "test@example.com"
}
response = requests.post(
f"{self.base_url}/users",
json=user_data
)
assert response.status_code == 201
created_user = response.json()
assert created_user['name'] == user_data['name']
assert created_user['email'] == user_data['email']
@pytest.mark.parametrize("invalid_email", [
"invalid-email",
"@example.com",
"test@",
""
])
def test_invalid_email_validation(self, invalid_email):
user_data = {
"name": "Test User",
"email": invalid_email
}
response = requests.post(
f"{self.base_url}/users",
json=user_data
)
assert response.status_code == 400
JSON Testing Best Practices
1. Validate Response Structure
- Check required fields are present
- Validate data types
- Test nested objects and arrays
- Verify field constraints (min/max values)
2. Test Edge Cases
- Empty responses
- Large payloads
- Special characters in strings
- Null values
- Unicode content
3. Security Testing
- Test for SQL injection in JSON fields
- Validate input sanitization
- Check for sensitive data exposure
- Test authentication and authorization
Online JSON Testing Tools
For quick JSON validation and testing, use these online tools:
- JSON Validator - Validate JSON syntax
- JSON Formatter - Format and beautify JSON
- JSON Diff - Compare API responses
- Python Compiler - Test JSON parsing code
Conclusion
Effective API testing with JSON requires a combination of tools, techniques, and best practices. Start with basic validation, add schema checking, and gradually build comprehensive test suites. Remember to test both success and error scenarios, and always validate the JSON structure and data types.
Regular API testing ensures your applications remain reliable, secure, and performant as they evolve.