pact-python
pact-python is an active library (current version 3.2.1) providing consumer-driven contract testing capabilities for Python applications. It builds on the Pact Rust FFI library, offering full support for Pact features and ensuring compatibility with other Pact implementations. It sees regular updates, often aligning with major Pact specification changes and core library enhancements.
Warnings
- breaking Version 3.x introduces significant breaking changes due to a re-architecture leveraging the Pact Rust FFI library, replacing the older Ruby-based executables and API. The primary `pact` module now exposes the v3 API.
- breaking The default Pact specification version for new `Pact` instances changed from v3 to v4. While largely backward-compatible, explicitly setting the specification might be necessary if your project relies on specific v3 behaviors.
- breaking The signature of the `Interaction.given()` method has been simplified, which may require updates to consumer test definitions.
- deprecated The `pact.v3.ffi` module has been removed as of v3.0.0 and is replaced by the standalone `pact_ffi` package.
- gotcha When defining provider states using `given()`, it's recommended to parameterize the state (e.g., `given("user exists", id=123)`) rather than embedding values directly in the state description (e.g., `given("user 123 exists")`). This makes provider state handlers more reusable and robust.
Install
-
pip install pact-python
Imports
- Pact
from pact import Pact
- Consumer, Provider
from pact import Consumer, Provider
- Verifier
from pact.verifier import Verifier
Quickstart
import atexit
import unittest
import requests
import os
from pact import Pact
# Define the client that will interact with the provider
class UserClient:
def __init__(self, base_url):
self.base_url = base_url
def get_user(self, username):
response = requests.get(f"{self.base_url}/users/{username}")
response.raise_for_status()
return response.json()
# Set up Pact for consumer testing
# Use environment variables for consumer/provider names in CI/CD, or hardcode for local dev
PACT_MOCK_HOST = os.environ.get('PACT_MOCK_HOST', 'localhost')
PACT_MOCK_PORT = int(os.environ.get('PACT_MOCK_PORT', '1234'))
# Instantiate Pact with consumer and provider names
pact = Pact(
consumer=os.environ.get('PACT_CONSUMER', 'MyConsumer'),
provider=os.environ.get('PACT_PROVIDER', 'UserService')
)
# Start the mock service and register its shutdown
pact.start_service(host_name=PACT_MOCK_HOST, port=PACT_MOCK_PORT)
atexit.register(pact.stop_service)
class GetUserInfoContract(unittest.TestCase):
def test_get_ash_user(self):
expected_body = {
'username': 'Ash',
'id': 123,
'groups': ['Admin']
}
# Define the interaction
(pact
.given('User Ash exists and is an administrator')
.upon_receiving('a request for Ash')
.with_request('GET', '/users/Ash')
.will_respond_with(200, headers={'Content-Type': 'application/json'}, body=expected_body))
# Run the consumer code against the mock service
with pact:
client = UserClient(pact.uri)
result = client.get_user('Ash')
self.assertEqual(result, expected_body)
if __name__ == '__main__':
unittest.main()