Pragmatic Testing Framework for Python with BDD-style syntax and pluggable architecture
49
Pending
Does it follow best practices?
Impact
49%
1.08xAverage score across 10 eval scenarios
Pending
The risk profile of this skill
Powerful parameterization system supporting multiple parameter sets with optional decorators for enhanced functionality.
The core params decorator for creating multiple test instances with different input values.
class params:
"""
Parameterization decorator for creating multiple test instances.
Can be used as a decorator directly or with additional decorators
via class item access.
"""
def __init__(self, *args, **kwargs):
"""
Initialize parameterization with positional and keyword arguments.
Args:
*args: Positional arguments to pass to the test
**kwargs: Keyword arguments to pass to the test
"""
def __call__(self, fn: F) -> F:
"""
Apply parameters to the test function or class constructor.
Args:
fn: Function or class to parameterize
Returns:
The parameterized function/class
"""
def __class_getitem__(cls, item) -> Callable[..., Parameterized]:
"""
Create parameterization with additional decorators.
Args:
item: Single decorator or tuple of decorators
Returns:
Callable that creates Parameterized instance with decorators
"""from vedro import Scenario, params
class Scenario(vedro.Scenario):
subject = "mathematical operations"
@params(2, 3, 5) # a=2, b=3, expected=5
@params(10, 15, 25) # a=10, b=15, expected=25
@params(-5, 8, 3) # a=-5, b=8, expected=3
def __init__(self, a, b, expected):
self.a = a
self.b = b
self.expected = expected
def when_numbers_are_added(self):
self.result = self.a + self.b
def then_result_matches_expected(self):
assert self.result == self.expectedfrom vedro import scenario, params, given, when, then, ensure
@params("admin", 200)
@params("user", 200)
@params("guest", 403)
@scenario("API access with different user roles")
def test_api_access(role, expected_status):
@given(f"user with {role} role")
def setup():
return authenticate_user_with_role(role)
@when("user accesses protected resource")
def action(user):
return api_request("/protected-resource", auth=user.token)
@then(f"response has status {expected_status}")
def verification(response):
ensure(response.status_code).equals(expected_status)Support for keyword arguments in parameterization for more readable tests.
@params(username="admin", password="admin123", should_succeed=True)
@params(username="user", password="wrongpass", should_succeed=False)
@params(username="", password="", should_succeed=False)
@scenario("Login with various credentials")
def test_login(username, password, should_succeed):
@given("login credentials")
def setup():
return {"username": username, "password": password}
@when("user attempts login")
def action(credentials):
try:
result = login(credentials["username"], credentials["password"])
return {"success": True, "result": result}
except AuthenticationError as e:
return {"success": False, "error": str(e)}
@then("login outcome matches expectation")
def verification(outcome):
ensure(outcome["success"]).equals(should_succeed)Advanced parameterization that applies additional decorators to specific parameter sets.
from vedro import skip
# Apply skip decorator to specific parameter sets
@params[skip("Flaky test")]("slow_endpoint", 30)
@params("fast_endpoint", 5)
@params("medium_endpoint", 15)
@scenario("API response times")
def test_response_times(endpoint, max_time):
@when(f"requesting {endpoint}")
def action():
start_time = time.time()
response = api_request(f"/api/{endpoint}")
duration = time.time() - start_time
return {"response": response, "duration": duration}
@then(f"response time is under {max_time} seconds")
def verification(result):
ensure(result["response"].status_code).equals(200)
ensure(result["duration"]).is_less_than(max_time)Apply multiple decorators to parameterized tests.
from vedro import skip_if, only
# Apply multiple decorators to parameter sets
@params[(skip_if(not EXTERNAL_API_AVAILABLE), only)](
"external_api", "https://api.external.com"
)
@params("internal_api", "http://localhost:8000")
@scenario("API integration tests")
def test_api_integration(api_name, base_url):
@when(f"calling {api_name}")
def action():
return api_request(f"{base_url}/health")
@then("API responds successfully")
def verification(response):
ensure(response.status_code).equals(200)Internal class representing a parameterized test with arguments and decorators.
class Parameterized:
"""
Represents a parameterized function or method with stored arguments
and decorators that are applied when the function is invoked.
"""
def __init__(self, args, kwargs, decorators: Tuple): ...
def __call__(self, fn: F) -> F: ...For type checking support, params provides appropriate type annotations.
# Type-checking support
F = TypeVar("F", bound=Callable[..., Any])
ItemType = Union[Callable[[F], F], Tuple[Callable[[F], F], ...]]Use complex objects as parameters for comprehensive test scenarios:
from dataclasses import dataclass
@dataclass
class UserProfile:
name: str
email: str
role: str
active: bool
@params(UserProfile("John Doe", "john@example.com", "admin", True))
@params(UserProfile("Jane Smith", "jane@example.com", "user", True))
@params(UserProfile("Bob Johnson", "bob@example.com", "guest", False))
@scenario("User profile management")
def test_user_profiles(profile):
@given("user profile data")
def setup():
return profile
@when("profile is processed")
def action(user_profile):
return process_user_profile(user_profile)
@then("profile is handled correctly")
def verification(result):
if profile.active:
ensure(result.status).equals("processed")
else:
ensure(result.status).equals("inactive")Generate parameters dynamically for data-driven tests:
# Generate test parameters from external data
test_cases = [
params(input_val, expected)
for input_val, expected in load_test_data("math_operations.json")
]
# Apply generated parameters
for param_decorator in test_cases:
@param_decorator
@scenario("Generated math test")
def test_generated_math(input_val, expected):
@when("calculation is performed")
def action():
return complex_calculation(input_val)
@then("result matches expected value")
def verification(result):
ensure(result).equals(expected)Skip or modify parameters based on runtime conditions:
import sys
# Conditionally apply parameters based on Python version
version_params = []
if sys.version_info >= (3, 9):
version_params.append(params("python39_feature", True))
if sys.version_info >= (3, 10):
version_params.append(params("python310_feature", True))
version_params.append(params("legacy_feature", True))
for param_decorator in version_params:
@param_decorator
@scenario("Version-specific features")
def test_version_features(feature_name, should_work):
@when(f"using {feature_name}")
def action():
return test_feature(feature_name)
@then("feature works as expected")
def verification(result):
ensure(result.success).equals(should_work)docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10