beerest: A Modern API Testing Framework for Python
Overview
beerest is a powerful, fluent Python library designed to simplify API testing while providing robust validation capabilities. It combines a clean, chainable syntax with comprehensive testing features to make API testing more intuitive and maintainable.
Features
- Fluent, chainable API for building requests
- Rich assertion library for response validation
- Built-in authentication support (Basic, Digest, Bearer Token)
- JSON Schema validation
- Response time assertions
- Custom validation functions
- JSONPath support for precise data access
Installation
pip install beerest
Quick Start
Basic Example
from beerest import Test, Expect
class TestAPI(Test):
def setup_method(self):
super().setup_method()
self.request.base_url = "https://api.example.com"
def test_get_user(self):
response = self.request.to("/users/1").get()
Expect(response) \
.status(200) \
.is_json() \
.body("name").equals("John Doe") \
.body("age").greater_than(18)
Core Components
Request Builder
Build HTTP requests with a fluent interface:
response = (request
.to("/api/users") # Set endpoint
.with_headers({"Content-Type": "application/json"}) # Add headers
.with_body({"name": "John"}) # Set request body
.with_query({"active": True}) # Add query parameters
.with_timeout(5.0) # Set timeout
.post() # Send request
)
Authentication Support
Multiple authentication methods available:
# Bearer Token
request.with_bearer_token("your-token")
# Basic Auth
request.with_basic_auth("username", "password")
# Digest Auth
request.with_digest_auth("username", "password")
# Custom Authentication
request.with_custom_auth(MyCustomAuth())
Response Validation
Comprehensive response validation with the Expect class:
Expect(response) \
.status(200) \
.body("data.users").has_type("array") \
.body("data.users").has_length(5) \
.body("metadata.total").greater_than(0) \
.header("Content-Type").contains("application/json") \
.time().less_than(500)
Schema Validation
Validate response structure against JSON schemas:
user_schema = {
"type": "object",
"required": ["id", "name", "email"],
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"email": {"type": "string"},
"age": {"type": "integer"}
}
}
Expect(response) \
.body() \
.matches_schema(user_schema)
Custom Validations
Create custom validation rules:
Expect(response) \
.body("users") \
.satisfies(
lambda users: all(user["age"] >= 18 for user in users),
"All users must be adults"
)
Advanced Features
JSONPath Support
Access nested data using JSONPath expressions:
Expect(response) \
.body("$.data[?(@.type=='user')].name") \
.contains("John")
Response Time Assertions
Monitor API performance:
Expect(response) \
.time() \
.less_than(500) # Response time under 500ms
Format Validation
Built-in format validators:
Expect(response) \
.body("email").matches_schema({"type": "string", "format": "email"}) \
.body("created_at").matches_schema({"type": "string", "format": "date-time-iso"}) \
.body("id").matches_schema({"type": "string", "format": "uuid"})
Best Practices
-
Test Organization
- Group related tests in test classes
- Use descriptive test method names
- Set up common configuration in
setup_method()
-
Request Structure
- Chain request methods for readability
- Set base URL in setup
- Use timeout for all requests
-
Assertions
- Use clear, specific assertions
- Chain validations logically
- Include both positive and negative tests
-
Error Handling
- Test error responses
- Validate error messages
- Check edge cases
Example Test Suite
class TestUserAPI(Test):
def setup_method(self):
super().setup_method()
self.request.base_url = "https://api.example.com"
def test_create_user(self):
"""Test user creation with valid data"""
user_data = {
"name": "John Doe",
"email": "john@example.com",
"age": 25
}
response = self.request \
.to("/users") \
.with_headers({"Content-Type": "application/json"}) \
.with_body(user_data) \
.post()
Expect(response) \
.status(201) \
.body("id").is_not_empty() \
.body("name").equals(user_data["name"]) \
.body("email").equals(user_data["email"])
def test_get_user_not_found(self):
"""Test handling of non-existent user"""
response = self.request.to("/users/999999").get()
Expect(response) \
.status(404) \
.body("error").equals("User not found")
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Release files for beerest 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| beerest-0.3.1.tar.gz | 14.4 kB | Details |
Release files / beerest-0.3.1.tar.gz
| Download URL | beerest-0.3.1.tar.gz |
|---|---|
| Size | 14.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
032755bde3105d0482a6ed202b3c90a0bbacaea8eca40b78b2be5e31ad63e46a
|
|
BLAKE2b-256 checksum How to use checksums |
48abaa257f1504e6125c1bd2bd31e68fdfa5d32c822ef12ddd641cc1ca9c48e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.12.1
|