Consumer test using Protobuf plugin with Pact Python v3.
This module demonstrates how to write a consumer test using the Pact protobuf
plugin with Pact Python's v3 API. The protobuf plugin allows Pact to handle
Protocol Buffer messages as request and response payloads, enabling contract
testing for services that communicate using protobuf serialization.
This example builds on the address book domain model from the protobuf.dev
tutorial and shows how to
test a consumer that retrieves Person data from a provider service using
protobuf-serialized messages over HTTP.
Classes
Functions
pact() -> Generator[Pact, None, None]
Set up the Pact fixture with protobuf plugin.
This fixture configures a Pact instance for consumer testing with the
protobuf plugin enabled. The protobuf plugin allows Pact to understand and
handle Protocol Buffer message serialization for both request and response
payloads.
The fixture uses the V4 specification which provides full support for
plugins and content type matching.
YIELDS |
DESCRIPTION |
Pact
|
The configured Pact instance for protobuf consumer tests.
|
Source code in examples/v3/plugins/protobuf/test_consumer.py
| @pytest.fixture
def pact() -> Generator[Pact, None, None]:
"""
Set up the Pact fixture with protobuf plugin.
This fixture configures a Pact instance for consumer testing with the
protobuf plugin enabled. The protobuf plugin allows Pact to understand and
handle Protocol Buffer message serialization for both request and response
payloads.
The fixture uses the V4 specification which provides full support for
plugins and content type matching.
Yields:
The configured Pact instance for protobuf consumer tests.
"""
pact_dir = Path(__file__).parents[3] / "pacts"
pact = (
Pact("protobuf_consumer", "protobuf_provider")
.with_specification("V4")
.using_plugin("protobuf", "0.3.15")
)
yield pact
pact.write_file(pact_dir)
|
test_get_nonexistent_person(pact: Pact) -> None
Test retrieving a non-existent Person by ID.
This test verifies the provider's behavior when requesting a person that
doesn't exist in the address book. The provider should return a 404 status
code with an appropriate error message as a JSON response.
Source code in examples/v3/plugins/protobuf/test_consumer.py
| def test_get_nonexistent_person(pact: Pact) -> None:
"""
Test retrieving a non-existent Person by ID.
This test verifies the provider's behavior when requesting a person that
doesn't exist in the address book. The provider should return a 404 status
code with an appropriate error message as a JSON response.
"""
(
pact.upon_receiving("a request to get non-existent person")
.given("person with ID 999 does not exist")
.with_request("GET", "/person/999")
.will_respond_with(404)
.with_header("Content-Type", "application/json")
.with_body({"detail": "Person not found"})
)
with pact.serve() as srv:
# NOTE: Again, we use the `requests` library to simulate the consumer's
# request to the provider service. A real-world consumer would instead
# use its own client and check that the appropriate error message is
# raised and/or handled.
response = requests.get(f"{srv.url}/person/999", timeout=5)
assert response.status_code == 404
assert response.headers["Content-Type"] == "application/json"
assert response.json() == {"detail": "Person not found"}
|
test_get_person_by_id(pact: Pact) -> None
Test retrieving a Person by ID using protobuf serialization.
This test defines the expected interaction for a GET request to retrieve a
specific person from the address book. The response will be a protobuf-
serialized Person message.
The test demonstrates:
- Using the protobuf plugin to handle binary protobuf content
- Matching on protobuf message structure and content
- Deserializing the protobuf response for validation
The provider state ensures that a person with ID 1 exists in the system,
corresponding to Alice from our sample address book.
Source code in examples/v3/plugins/protobuf/test_consumer.py
| def test_get_person_by_id(pact: Pact) -> None:
"""
Test retrieving a Person by ID using protobuf serialization.
This test defines the expected interaction for a GET request to retrieve a
specific person from the address book. The response will be a protobuf-
serialized Person message.
The test demonstrates:
- Using the protobuf plugin to handle binary protobuf content
- Matching on protobuf message structure and content
- Deserializing the protobuf response for validation
The provider state ensures that a person with ID 1 exists in the system,
corresponding to Alice from our sample address book.
"""
sample_address_book = address_book()
alice = sample_address_book.people[0]
expected_protobuf_data = alice.SerializeToString()
(
pact.upon_receiving("a request to get person by ID")
.given("person with ID 1 exists")
.with_request("GET", "/person/1")
.will_respond_with(200)
.with_header("Content-Type", "application/x-protobuf")
.with_binary_body(expected_protobuf_data, "application/x-protobuf")
)
with pact.serve() as srv:
# NOTE: We use the `requests` library here to demonstrate the
# principles; however, in a real-world scenario, you would be using the
# actual client code that interacts with the provider service. This
# ensures that you are testing the consumer's behaviour.
response = requests.get(f"{srv.url}/person/1", timeout=5)
# Verify response
assert response.status_code == 200
assert response.headers["Content-Type"] == "application/x-protobuf"
# Deserialize the protobuf response and then verify its content
person = Person()
person.ParseFromString(response.content)
assert person.id == 1
assert person.name == "Alice"
assert person.email == "alice@gmail.com"
assert len(person.phones) == 2
assert person.phones[0].number == "123-456-7890"
assert person.phones[0].type == Person.PhoneType.PHONE_TYPE_HOME
assert person.phones[1].number == "987-654-3210"
assert person.phones[1].type == Person.PhoneType.PHONE_TYPE_MOBILE
|