Skip to content

Commit 5994c3a

Browse files
pact-python manages the mock service for the user
- Extracted the logic configuring the mock service and verifying interactions to methods for users if they need more control than a context manager provides
1 parent 4bf7b8b commit 5994c3a

File tree

4 files changed

+170
-102
lines changed

4 files changed

+170
-102
lines changed

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ from pact import Consumer, Provider
4747

4848

4949
pact = Consumer('Consumer').has_pact_with(Provider('Provider'))
50-
pact.start()
51-
atexit.register(pact.stop)
50+
pact.start_service()
51+
atexit.register(pact.stop_service)
5252

5353

5454
class GetUserInfoContract(unittest.TestCase):
@@ -82,7 +82,22 @@ This does a few important things:
8282
Using the Pact object as a [context manager], we call our method under test
8383
which will then communicate with the Pact mock service. The mock service will respond with
8484
the items we defined, allowing us to assert that the method processed the response and
85-
returned the expected value.
85+
returned the expected value. If you want more control over when the mock service is
86+
configured and the interactions verified, use the `setup` and `verify` methods, respectively:
87+
88+
```python
89+
(pact
90+
.given('UserA exists and is not an administrator')
91+
.upon_receiving('a request for UserA')
92+
.with_request('get', '/users/UserA')
93+
.will_respond_with(200, body=expected))
94+
95+
pact.setup()
96+
# Some additional steps before running the code under test
97+
result = user('UserA')
98+
# Some additional steps before verifying all interactions have occurred
99+
pact.verify()
100+
````
86101

87102
The default hostname and port for the Pact mock service will be
88103
`localhost:1234` but you can adjust this during Pact creation:

e2e/contracts/test_e2e.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010

1111
pact = Consumer('consumer').has_pact_with(Provider('provider'))
12-
pact.start()
13-
atexit.register(pact.stop)
12+
pact.start_service()
13+
atexit.register(pact.stop_service)
1414

1515

1616
class BaseTestCase(unittest.TestCase):

pact/pact.py

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,29 @@ def given(self, provider_state):
106106
self._provider_state = provider_state
107107
return self
108108

109-
def start(self):
109+
def setup(self):
110+
"""Configure the Mock Service to ready it for a test."""
111+
try:
112+
payload = {
113+
'description': self._description,
114+
'provider_state': self._provider_state,
115+
'request': self._request,
116+
'response': self._response
117+
}
118+
119+
resp = requests.delete(
120+
self.uri + '/interactions', headers=self.HEADERS)
121+
122+
assert resp.status_code == 200, resp.content
123+
resp = requests.post(
124+
self.uri + '/interactions',
125+
headers=self.HEADERS, json=payload)
126+
127+
assert resp.status_code == 200, resp.content
128+
except AssertionError:
129+
raise
130+
131+
def start_service(self):
110132
"""Start the external Mock Service."""
111133
command = [
112134
MOCK_SERVICE_PATH,
@@ -131,7 +153,7 @@ def start(self):
131153
if process.returncode != 0:
132154
raise RuntimeError('The Pact mock service failed to start.')
133155

134-
def stop(self):
156+
def stop_service(self):
135157
"""Stop the external Mock Service."""
136158
command = [MOCK_SERVICE_PATH, 'stop', '--port={}'.format(self.port)]
137159
popen = Popen(command)
@@ -151,6 +173,28 @@ def upon_receiving(self, scenario):
151173
self._description = scenario
152174
return self
153175

176+
def verify(self):
177+
"""
178+
Have the mock service verify all interactions occurred.
179+
180+
Calls the mock service to verify that all interactions occurred as
181+
expected, and has it write out the contracts to disk.
182+
183+
:raises AssertionError: When not all interactions are found.
184+
"""
185+
resp = requests.get(
186+
self.uri + '/interactions/verification',
187+
headers=self.HEADERS)
188+
assert resp.status_code == 200, resp.content
189+
payload = {
190+
'consumer': {'name': self.consumer.name},
191+
'provider': {'name': self.provider.name},
192+
'pact_dir': self.pact_dir
193+
}
194+
resp = requests.post(
195+
self.uri + '/pact', headers=self.HEADERS, json=payload)
196+
assert resp.status_code == 200, resp.content
197+
154198
def with_request(self, method, path, body=None, headers=None, query=None):
155199
"""
156200
Define the request the request that the client is expected to perform.
@@ -198,25 +242,7 @@ def __enter__(self):
198242
199243
Sets up the mock service to expect the client requests.
200244
"""
201-
try:
202-
payload = {
203-
'description': self._description,
204-
'provider_state': self._provider_state,
205-
'request': self._request,
206-
'response': self._response
207-
}
208-
209-
resp = requests.delete(
210-
self.uri + '/interactions', headers=self.HEADERS)
211-
212-
assert resp.status_code == 200, resp.content
213-
resp = requests.post(
214-
self.uri + '/interactions',
215-
headers=self.HEADERS, json=payload)
216-
217-
assert resp.status_code == 200, resp.content
218-
except AssertionError:
219-
raise
245+
self.setup()
220246

221247
def __exit__(self, exc_type, exc_val, exc_tb):
222248
"""
@@ -228,18 +254,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
228254
if (exc_type, exc_val, exc_tb) != (None, None, None):
229255
return
230256

231-
resp = requests.get(
232-
self.uri + '/interactions/verification',
233-
headers=self.HEADERS)
234-
assert resp.status_code == 200, resp.content
235-
payload = {
236-
'consumer': {'name': self.consumer.name},
237-
'provider': {'name': self.provider.name},
238-
'pact_dir': self.pact_dir
239-
}
240-
resp = requests.post(
241-
self.uri + '/pact', headers=self.HEADERS, json=payload)
242-
assert resp.status_code == 200, resp.content
257+
self.verify()
243258

244259

245260
class FromTerms(object):

0 commit comments

Comments
 (0)