John Williams
06/23/2022, 6:15 AMI, [2022-06-23T16:14:14.635384 #84978] INFO -- : Cleared interactions
I, [2022-06-23T16:14:14.663371 #84978] INFO -- : Registered expected interaction GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
D, [2022-06-23T16:14:14.663741 #84978] DEBUG -- : {
"description": "a request for cancellation",
"providerState": "A scheduled order exists and it is not cancelled already",
"request": {
"method": "get",
"path": "<http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel"
},
"response": {
"status": 200,
"headers": {
},
"body": {
"json_class": "Pact::SomethingLike",
"contents": {
"id": "1e54e244-d0ab-46ed-a88a-b9e6037655ef",
"order": [
{
"product": "coffee",
"quantity": 1,
"size": "small"
}
],
"scheduled": "Wed, 22 Jun 2022 09:21:26 GMT",
"status": "cancelled"
}
}
},
"metadata": null
}
W, [2022-06-23T16:14:14.721886 #84978] WARN -- : Verifying - actual interactions do not match expected interactions.
Missing requests:
GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
W, [2022-06-23T16:14:14.721961 #84978] WARN -- : Missing requests:
GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
Matt (pactflow.io / pact-js / pact-go)
That should give you a hint.Copy code<http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
with_request('get', f'<http://localhost:3001/kitchen/schedule/{Like(12343)}/cancel>')
You probably need to do something like this:
with_request('get', Regex('<http://localhost:3001/kitchen/schedule/([0-9+])/cancel>')
(that’s not a real regex and I’m not sure that’s the right function to do a regex, but hopefully that gives you an idea)John Williams
06/23/2022, 7:38 AMJohn Williams
06/23/2022, 9:20 AMpact.with_request(
method='POST',
path='/api/v1/my-resources/123',
body={'user_ids': [1, 2, 3]},
headers={'Content-Type': 'application/json'},
)
In the pact log, where is the actal request and which is the expected it isn't very clear?
I tried just adding hte path instead of the whole url.John Williams
06/23/2022, 9:22 AMI, [2022-06-23T19:21:41.781641 #92156] INFO -- : Cleared interactions
I, [2022-06-23T19:21:41.798136 #92156] INFO -- : Registered expected interaction GET /kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel
D, [2022-06-23T19:21:41.798505 #92156] DEBUG -- : {
"description": "a request for cancellation",
"providerState": "A scheduled order exists and it is not cancelled already",
"request": {
"method": "GET",
"path": "/kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel"
},
"response": {
"status": 200,
"headers": {
},
"body": {
"json_class": "Pact::SomethingLike",
"contents": {
"id": "1e54e244-d0ab-46ed-a88a-b9e6037655ef",
"order": [
{
"product": "coffee",
"quantity": 1,
"size": "small"
}
],
"scheduled": "Wed, 22 Jun 2022 09:21:26 GMT",
"status": "cancelled"
}
}
},
"metadata": null
}
W, [2022-06-23T19:21:41.848351 #92156] WARN -- : Verifying - actual interactions do not match expected interactions.
Missing requests:
GET /kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel
W, [2022-06-23T19:21:41.848412 #92156] WARN -- : Missing requests:
GET /kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel
Mike Geeves
06/23/2022, 10:39 AMJohn Williams
06/23/2022, 10:41 AMJohn Williams
06/23/2022, 10:44 AMMike Geeves
06/23/2022, 10:46 AM@pytest.fixture
def consumer() -> Order.cancel:
return Order.cancel("http://{host}:{port}".format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT))
so you are trying to call Order.cancel with the argument of the url, you aren't creating Order anywhere I can seeJohn Williams
06/23/2022, 10:47 AMimport atexit
from datetime import datetime
import logging
import os
from uuid import UUID
import requests
import pytest
import subprocess
import re
from pact import Consumer, Like, Provider, Term, Format, EachLike
from orders_service.orders import Order, OrderItem
log = logging.getLogger(__name__)
logging.basicConfig(level=<http://logging.INFO|logging.INFO>)
# If publishing the Pact(s), they will be submitted to the Pact Broker here.
# For the purposes of this example, the broker is started up as a fixture defined
# in conftest.py. For normal usage this would be self-hosted or using Pactflow.
PACT_BROKER_URL = "<https://xxx.pactflow.io/>"
PACT_BROKER_USERNAME = "xxx"
PACT_BROKER_PASSWORD = "xxx"
# Define where to run the mock server, for the consumer to connect to. These
# are the defaults so may be omitted
PACT_MOCK_HOST = "localhost"
PACT_MOCK_PORT = 1234
# Where to output the JSON Pact files created by any tests
PACT_DIR = os.path.dirname(os.path.realpath(__file__))
@pytest.fixture
def consumer() -> Order.cancel:
# return Order("http://{host}:{port}".format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT))
order = [{"id":"43b2ee86-9c06-45a0-a281-6c136e3bdd7b", "product":"coffee", "size":"big", "quantity":1}]
# payload = Order(id="76ac3eb5-a656-49e3-b973-17e7c992cc5e", created="2022-06-23T03:11:19.415574", items=order, status="created").dict()
# print("first payload: ",payload)
# return Order(payload)
return Order(id="43b2ee86-9c06-45a0-a281-6c136e3bdd7b", created="2022-06-23T05:59:02.948144", items=order, status="cancelled")
@pytest.fixture(scope="session")
def pact(request):
"""Setup a Pact Consumer, which provides the Provider mock service. This
will generate and optionally publish Pacts to the Pact Broker"""
# When publishing a Pact to the Pact Broker, a version number of the Consumer
# is required, to be able to construct the compatability matrix between the
# Consumer versions and Provider versions
# version = request.config.getoption("--publish-pact")
# publish = True if version else False
pact = Consumer("UserServiceClient", version=1).has_pact_with(
Provider("UserService"),
host_name=PACT_MOCK_HOST,
port=PACT_MOCK_PORT,
pact_dir=PACT_DIR,
publish_to_broker=True,
broker_base_url=PACT_BROKER_URL,
broker_username=PACT_BROKER_USERNAME,
broker_password=PACT_BROKER_PASSWORD,
)
pact.start_service()
# Make sure the Pact mocked provider is stopped when we finish, otherwise
# port 1234 may become blocked
atexit.register(pact.stop_service)
yield pact
# This will stop the Pact mock server, and if publish is True, submit Pacts
# to the Pact Broker
pact.stop_service()
# Given we have cleanly stopped the service, we do not want to re-submit the
# Pacts to the Pact Broker again atexit, since the Broker may no longer be
# available if it has been started using the --run-broker option, as it will
# have been torn down at that point
pact.publish_to_broker = False
def test_cancel_scheduled_order(pact, consumer):
expected = {
"id": Format().uuid(),
"order": [
{
"product": "coffee",
"quantity": 1,
"size": "big"
}
],
"scheduled": Format().date(),
"status": "cancelled"
}
(pact
.given('A scheduled order exists and it is not cancelled already')
.upon_receiving('a request for cancellation')
.with_request(method='GET', path='<http://127.0.0.1:5000/kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel>', headers={'Content-Type': 'application/json'},)
.will_respond_with(200, body=Like(expected)))
with pact:
# order = [{"id":"43b2ee86-9c06-45a0-a281-6c136e3bdd7b", "product":"coffee", "size":"big", "quantity":1}]
# payload = Order(id="43b2ee86-9c06-45a0-a281-6c136e3bdd7b", created="2022-06-23T06:00:24.667545", items=order, status="pending").dict()
# return Order.cancel(payload)
# payload = Order(UUID, datetime.now, {"product":"coffee", "size":"large", "quantity":1}, "progress")
# print("payload is: ",payload)
response = consumer.cancel()
# resp = response.json()
# print("resp: ", response.json())
# assert resp['status'] == "cancelled"
# assert response['status'] == "cancelled"
pact.verify()
John Williams
06/23/2022, 10:47 AMreturn Order(id="43b2ee86-9c06-45a0-a281-6c136e3bdd7b", created="2022-06-23T05:59:02.948144", items=order, status="cancelled")
John Williams
06/23/2022, 10:50 AMMike Geeves
06/23/2022, 11:48 AMJohn Williams
06/23/2022, 12:15 PMJohn Williams
06/23/2022, 12:16 PMJohn Williams
06/23/2022, 12:17 PMJohn Williams
06/23/2022, 12:17 PMJohn Williams
06/23/2022, 12:18 PMMike Geeves
06/23/2022, 12:18 PMso i create the order hewre:
return Order(id="43b2ee86-9c06-45a0-a281-6c136e3bdd7b", created="2022-06-23T05:59:02.948144", items=order, status="cancelled")
you're creating it as cancelledJohn Williams
06/23/2022, 12:20 PMJohn Williams
06/23/2022, 12:20 PMJohn Williams
06/23/2022, 12:21 PMMatt (pactflow.io / pact-js / pact-go)
In the pact log, where is the actal request and which is the expected it isn’t very clear?It should say “Registered expected interaction” for the expected when, and the “Received” when it receives a call. In your log above, it never received a call - so that would indicate your code is not sending the request to the mock service (which I think you know now, but writing for posterity)
Matt (pactflow.io / pact-js / pact-go)
.with_request(method=‘GET’, path=‘http://127.0.0.1:5000/kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel’, headers={‘Content-Type’: ‘application/json’},)You’re right btw, you should only have the path here, not the full URI
Matt (pactflow.io / pact-js / pact-go)
(pact
.given('A scheduled order exists and it is not cancelled already')
.upon_receiving('a request for cancellation')
.with_request(method='GET', path='/kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel', headers={'Content-Type': 'application/json'},)
.will_respond_with(200, body=Like(expected)))
with pact:
// this needs to be sending a request to
// <http://localhost:1234/kitchen/schedule/bc72e917-4af1-4e39-b897-1eda6d006b18/cancel>
response = consumer.cancel()
pact.verify()
Matt (pactflow.io / pact-js / pact-go)
@pytest.fixture
def consumer() -> Order.cancel:
# return Order("http://{host}:{port}".format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT))
order = [{"id":"43b2ee86-9c06-45a0-a281-6c136e3bdd7b", "product":"coffee", "size":"big", "quantity":1}]
# payload = Order(id="76ac3eb5-a656-49e3-b973-17e7c992cc5e", created="2022-06-23T03:11:19.415574", items=order, status="created").dict()
# print("first payload: ",payload)
# return Order(payload)
return Order(id="43b2ee86-9c06-45a0-a281-6c136e3bdd7b", created="2022-06-23T05:59:02.948144", items=order, status="cancelled")
Assuming the consumer.cancel()
calls this code, this would explain why the mock service isn’t seeing any requests, because it’s not making any requestsJohn Williams
06/23/2022, 11:01 PMJohn Williams
06/24/2022, 6:05 AMwith pact:
requests.get(
f'<http://localhost:1234/kitchen/schedule/43b2ee86-9c06-45a0-a281-6c136e3bdd7b/cancel>',
data={'order': [{"id":"43b2ee86-9c06-45a0-a281-6c136e3bdd7b", "product":"coffee", "size":"big", "quantity":1}]})
response = consumer.cancel()
response = response.json()
assert response
['status'] == "cancelled"
pact.verify()
I though that: response = consumer.cancel()
would make the call to the mock server as i defined the Object which the cancel method is a apart of in the python fixture.
I didnt realise that I need to make the request to the mock server basically.
I just need to sort out my api bearer token and i should be able to publish that>John Williams
06/24/2022, 6:06 AMMatt (pactflow.io / pact-js / pact-go)
# 1
requests.get(
f'<http://localhost:1234/kitchen/schedule/43b2ee86-9c06-45a0-a281-6c136e3bdd7b/cancel>',
data={'order': [{"id":"43b2ee86-9c06-45a0-a281-6c136e3bdd7b", "product":"coffee", "size":"big", "quantity":1}]})
# 2
response = consumer.cancel()
# 3
response = response.json()
1. should really be done as part of 2.
2. You should obviously also add assertions to 3
The whole idea here is that you’re unit testing an API client. Presumably, consumer
is supposed to be an API client?Mike Geeves
06/24/2022, 7:57 AMJohn Williams
06/24/2022, 8:30 AMMatt (pactflow.io / pact-js / pact-go)
function
call, which is the thing that calls the API. It needs to be configurable to talk to the mock service during the unit testJohn Williams
06/24/2022, 9:09 AMJohn Williams
06/24/2022, 9:10 AMJohn Williams
06/24/2022, 9:12 AMMatt (pactflow.io / pact-js / pact-go)
John Williams
06/24/2022, 9:13 AMJohn Williams
06/24/2022, 9:17 AMMatt (pactflow.io / pact-js / pact-go)
and it isnt needed at all in the test to call the real endpoint? (edited)correct. You usually would dynamically configure the API client to talk to the Pact mock server instead of the real thing. This way it’s close to being deterministic, much more reliable, and you don’t need to stand up the real test environment to run it (isolation).
Matt (pactflow.io / pact-js / pact-go)
i guess that the provider test will make that requestYes. The provider test will re-issue what you put in your consumer test, against the locally running provider in a unit test like environment for the provider side (again, aiming for isolation for deterministic/reliable tests)
John Williams
06/24/2022, 11:26 AM