Skip to content

Commit 3898aee

Browse files
committed
Created examples folder
Moved over initial test files for e2e test test found and running First test passing Created simple consumer tests Getting consumer tests a bit more sturdy Readme for example First pass at provider tests working README on error case Update README.md Update README.md Update README.md Script now shuts down a bit better Publishing consumer to broker Provider now working (or not working) Fixing flake8 errors Added Readme link to examples Moved files under src to better simulate a real project Mixin for pact urls flake8 fixes Some doco updates Added better trap for script example will either use broker or local now Travis changes including running e2e for 3.6 Minor readme change
1 parent b859443 commit 3898aee

16 files changed

+411
-38
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ script:
2020
- flake8
2121
- pydocstyle pact
2222
- tox
23-
- if [[ $TRAVIS_PYTHON_VERSION == "2.7" ]]; then make package && pip install ./dist/pact-python-*.tar.gz && make e2e; fi
23+
- if [[ $TRAVIS_PYTHON_VERSION == "3.6" ]]; then make package && pip install ./dist/pact-python-*.tar.gz && make e2e; fi
2424

2525
before_deploy:
2626
- export RELEASE_PACKAGE=$(ls dist/pact-python-*.tar.gz)

Makefile

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,42 +29,10 @@ deps:
2929

3030

3131
define E2E
32-
set -ex
33-
cd e2e
34-
nosetests ./contracts
35-
python app.py &
36-
APP_PID=$$!
37-
function teardown {
38-
echo 'Tearing down Flask server'
39-
kill $$APP_PID
40-
}
41-
trap teardown EXIT
42-
while ! nc -z localhost 5000; do
43-
sleep 0.1
44-
done
45-
46-
set +e
47-
pact-verifier \
48-
--provider-base-url=http://localhost:5000 \
49-
--provider-states-url=http://localhost:5000/_pact/provider-states \
50-
--provider-states-setup-url=http://localhost:5000/_pact/provider-states/active \
51-
./pacts/failing-consumer-provider.json
52-
EXIT_CODE=$$?
53-
set -e
54-
55-
if [ $$EXIT_CODE -eq 1 ]; then
56-
echo "Failing verification exited with 1 as expected"
57-
else
58-
echo "Failing verification did not exit with 1 as expected"
59-
exit 1
60-
fi
61-
62-
pact-verifier \
63-
--provider-base-url=http://localhost:5000 \
64-
--provider-states-url=http://localhost:5000/_pact/provider-states \
65-
--provider-states-setup-url=http://localhost:5000/_pact/provider-states/active \
66-
./pacts/consumer-provider.json
67-
32+
cd examples/e2e
33+
pip install -r requirements.txt
34+
pytest tests/test_user_consumer.py
35+
./verify_pact.sh
6836
endef
6937

7038

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ test your code more efficiently, check out the [Pact documentation].
1919
pip install pact-python
2020
```
2121

22+
## Getting started
23+
24+
A guide follows but if you go to the [e2e examples](examples/e2e/README.md). This has a consumer, provider and pact-broker set of tests.
25+
2226
## Writing a Pact
27+
2328
Creating a complete contract is a two step process:
2429

2530
1. Create a test on the consumer side that declares the expectations it has of the provider
@@ -236,7 +241,7 @@ EachLike({
236241
> Note, you do not need to specify everything that will be returned from the Provider in a
237242
> JSON response, any extra data that is received will be ignored and the tests will still pass.
238243
239-
> Note, to get the generated values from an object that can contain matchers like Term, Like, EachLike, etc.
244+
> Note, to get the generated values from an object that can contain matchers like Term, Like, EachLike, etc.
240245
> for assertion in self.assertEqual(result, expected) you may need to use get_generated_values() helper function:
241246
242247
```python

examples/e2e/.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"python.testing.pytestArgs": [
3+
"tests"
4+
],
5+
"python.testing.unittestEnabled": false,
6+
"python.testing.nosetestsEnabled": false,
7+
"python.testing.pytestEnabled": true
8+
}

examples/e2e/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Introduction
2+
3+
This is an e2e example using flask to help as a guide to getting started with Pact for Python. For the provider it imports the app into a wrapper (pact_provider.py) so as to decorate extra functions we can use to test our app.
4+
5+
## Setup
6+
7+
Create your own virtualenv for this. Run
8+
9+
```bash
10+
pip install -r requirements.txt
11+
pip install pact-python
12+
```
13+
14+
TODO: Make this so you can point to the parent install to help with development.
15+
16+
Create the local broker (for demo purposes only) To do this separately clone this repo:
17+
* https://github.com/pact-foundation/pact-broker-docker
18+
19+
Then from where this is install run in it's own terminal
20+
21+
```bash
22+
docker-compose up
23+
```
24+
25+
If you can open a browser to http://localhost and see the broker you have succeeded.
26+
27+
## Consumer
28+
29+
From the root directory run:
30+
31+
```bash
32+
pytest
33+
```
34+
35+
Or you can run individual tests like:
36+
37+
```bash
38+
pytest tests/test_user_consumer.py::test_get_non_existing_user
39+
```
40+
41+
If you want to publish this to the pact broker add the '--publish-pact' option like:
42+
43+
```bash
44+
pytest --publish-pact=XX
45+
```
46+
47+
XX is the version number of the pact and is for you to manage in your deployment process.
48+
49+
Sometimes you may get the mock server in a hung state. You can kill it via (untested):
50+
51+
```bash
52+
pkill -f pact-mock-service.rb
53+
```
54+
55+
## Provider States
56+
57+
Run the script (placeholder version number for pact broker)
58+
59+
```bash
60+
./verify_pact.sh 1
61+
```
62+
63+
This will import the provider.py file which is the actual app and then decorate it with extra urls. It then puts this into the background and runs the pact-verifier tool against it.
64+
65+
To test what this looks like when failing change one of these values.
66+
67+
```python
68+
def setup_user_a_nonadmin():
69+
70+
id = '00000000-0000-4000-a000-000000000000'
71+
some_date = '2016-12-15T20:16:01'
72+
73+
```
74+
75+
### Provider debugging
76+
77+
To manually trigger one of the 2 manual states you can run:
78+
79+
```bash
80+
curl -X POST -H "Content-Type: application/json" --data "{\"state\": \"UserA exists and is not an administrator\"}" http://127.0.0.1:5000/_pact/provider_states
81+
```
82+
83+
Changing the json content to match the state you want.

examples/e2e/__init__.py

Whitespace-only changes.

examples/e2e/pact_provider.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from flask import jsonify, request
2+
3+
from src.provider import app, fakedb
4+
5+
6+
@app.route('/_pact/provider_states', methods=['POST'])
7+
def provider_states():
8+
mapping = {'UserA does not exist': setup_no_user_a,
9+
'UserA exists and is not an administrator':
10+
setup_user_a_nonadmin}
11+
mapping[request.json['state']]()
12+
return jsonify({'result': request.json['state']})
13+
14+
15+
def setup_no_user_a():
16+
if 'UserA' in fakedb:
17+
del fakedb['UserA']
18+
19+
20+
def setup_user_a_nonadmin():
21+
id = '00000000-0000-4000-a000-000000000000'
22+
some_date = '2016-12-15T20:16:01'
23+
24+
fakedb['UserA'] = {
25+
'name': "UserA",
26+
'id': id,
27+
'created_on': some_date,
28+
'admin': False
29+
}
30+
31+
32+
if __name__ == '__main__':
33+
app.run(debug=True, port=5001)

examples/e2e/requirements.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
attrs==19.3.0
2+
certifi==2019.11.28
3+
chardet==3.0.4
4+
click==7.1.1
5+
Flask==1.1.1
6+
idna==2.9
7+
importlib-metadata==1.6.0
8+
itsdangerous==1.1.0
9+
Jinja2==2.11.1
10+
MarkupSafe==1.1.1
11+
more-itertools==8.2.0
12+
packaging==20.3
13+
pluggy==0.13.1
14+
psutil==5.7.0
15+
py==1.8.1
16+
pyparsing==2.4.6
17+
pytest==5.4.1
18+
requests==2.23.0
19+
six==1.14.0
20+
urllib3==1.25.8
21+
wcwidth==0.1.9
22+
Werkzeug==1.0.1
23+
zipp==3.1.0

examples/e2e/src/__init__.py

Whitespace-only changes.

examples/e2e/src/consumer.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import requests
2+
3+
4+
class UserConsumer(object):
5+
def __init__(self, base_uri):
6+
self.base_uri = base_uri
7+
8+
def get_user(self, user_name):
9+
"""Fetch a user object by user_name from the server."""
10+
uri = self.base_uri + '/users/' + user_name
11+
response = requests.get(uri)
12+
if response.status_code == 404:
13+
return None
14+
15+
name = response.json()['name']
16+
return User(name)
17+
18+
19+
class User(object):
20+
def __init__(self, name):
21+
self.name = name

0 commit comments

Comments
 (0)