Tavern API Testing Examples
“Tavern is a pytest plugin, command-line tool and Python library for automated testing of APIs, with a simple, concise and flexible YAML-based syntax” (https://taverntesting.github.io/)
I created GitHub repo containing 30+ simple Tavern test examples and decided to share it on my website. I think it can helpful for someone like me who has just started to learn Tavern or looking for a tool to automate API testing. You can find lots of examples in Tavern Documentation, but most of them you cannot just copy-paste and run to see the result (some examples require running server using Flask). Therefore I created a set of my own examples using free public APIs to help me go through Tavern documentation and learn how to use it:
- http://api.zippopotam.us/
- https://jsonplaceholder.typicode.com
- https://gorest.co.in/
- http://www.recipepuppy.com
- http://www.dropboxapi.com
Some examples of Tavern tests:
- Sending request and checking response status code, json response, headers
- Using external function to validate response
- Using built-in Tavern schema validators
- Test parametrization
- Multi stage tests
- Using external configuration files
- Uploading file
- Creating and reading resources using APIs
Tavern Docs and useful links
Feel free to read Tavern Documentation. It is well written and has plenty of examples.
There are not many articles and tutorials on how to use Tavern. Here are a few links that I found:
- https://www.ontestautomation.com/writing-api-tests-in-python-with-tavern/
- https://medium.com/@ali.muhammadimran/rest-api-test-automation-using-python-with-tavern-ci-part-1-707026eae702
- https://apagiaro.it/tavern-test-api/
Getting Started
If you clone this repo and use PyCharm:
- clone repo
- set up virtual environment
- install dependancies (pip install -r requirements.txt)
- install colorlog
- in PyCharm set pytest as default test runner (preferences-tools-python integrated tools-testing-pytest-apply)
- make sure your yaml test is called test_x.tavern.yaml, where x should be a description of the contained tests
- edit conftest.py and replace my API keys with yours:
I used dotenv to inject my API keys in tests, so you will need to disable this section in conftest.py:
# conftest.py
# =========== disable this section and obtain your own API keys to run tests on your machine ================
# =========== see comments in test_basics.tavern.yaml, test_http.tavern.yaml ================================
try:
load_dotenv()
API_KEY_DROPBOX = os.getenv('API_KEY_DROPBOX')
API_KEY_GOREST = os.getenv('API_KEY_GOREST')
except Exception as e:
logging.info(f'disable try-except section at the top of conftest.py and obtain your own API keys to run tests '
f'on your machine: {e}')
# =====================================================================================================
Then replace variables with your API_KEYs, example:
stages:
- name: Authenticate and add new random user
request:
url: https://gorest.co.in/public-api/users
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY, example:
# Authorization: "Bearer 234dflkjdf967lkjdsf"
# go to https://gorest.co.in/user/login.html to register free account
If you start your own project and use PyCharm:
- create new project
- set up virtual environment
- install tavern
- install colorlog
- in PyCharm set pytest as default test runner (preferences-tools-python integrated tools-testing-pytest-apply)
- make sure your yaml tests are names use the following pattern test_x.tavern.yaml, where x should be a description of the contained tests
- feel free to use my contest.py and pytest.ini to make test output friendlier.
Project structure example
Below is the structure of my project folder
.
├── LICENSE
├── __pycache__
│ ├── conftest.cpython-38-pytest-5.4.1.pyc
│ └── conftest.cpython-38-pytest-5.4.2.pyc
├── accounts.yaml
├── conftest.py
├── logging.yaml
├── pytest.ini
├── readme.md
├── requirements.txt
├── tavern-demo.gif
├── test_data
│ └── test_pdf.pdf
├── tests
│ ├── __init__.py
│ ├── __pycache__
│ ├── api_urls.yaml
│ ├── common.yaml
│ ├── includes.yaml
│ ├── includesA.yaml
│ ├── includesB.yaml
│ ├── test_basics.tavern.yaml
│ ├── test_http.tavern.yaml
│ ├── utils.py
│ └── yaml_basics.yaml
├── venv
│ ├── bin
│ ├── include
│ ├── lib
│ └── pyvenv.cfg
└── zip_code.yaml
Some common mistakes to avoid
Of course you can read the docs, but I decided to share some of my mistakes. Chances are you have the same problem which can be fixed quite quickly if you know where to look at.
Adding folder to PYTHONPATH
To make sure that Tavern can find external functions you need to make sure that it is in the Python path. I had some issues with adding my test dir to PYTHONPATH. Seems like in different shells this command may vary. This is what works / does not work for me (I use zsh). For example, if utils.py is in the ‘tests’ folder:
Not worked:
PYTHONPATH=$PYTHONPATH:/tests pytest tests/test_basics.tavern.yaml -k ex04
...
E tavern.util.exceptions.InvalidExtFunctionError: Error importing module utils
PYTHONPATH=$PYTHONPATH:tests pytest tests/test_basics.tavern.yaml -k ex04
...
zsh: bad substitution
Worked:
PYTHONPATH=$PYTHONPATH:./tests pytest tests/test_basics.tavern.yaml -q -k ex04
You can modify ~/.bash_profile to add absolute path to your PYTHONPATH so you do not need to include PYTHONPATH in command each time (note, this might affect other projects, so just comment it out when you do not need it) . For Example:
export PYTHONPATH="$PYTHONPATH:/Users/maksim/repos/tau-tools-demo/py-tavern-api/tests"
# save and exit
# then run in shell or in PyCharm terminal:
source ~/.bashprofile
Printing entire response using logging
Add this hook to your contest.py file to be able to see response even if it returned not in json format. Very often it helps with debugging:
#conftest.py
def pytest_tavern_beta_after_every_response(expected, response):
try:
logging.info(f"================= RESPONSE ================== "
f"\n\nstatus code [{response.status_code}]\n{dumps(response.json(), indent=4)}\n\n")
except ValueError as e:
logging.info(f"================= RESPONSE ================== "
f"\n\nstatus code [{response.status_code}]\n{response.text,}\n\n")
return
Use –log-cli-level in command line to enable different logging levels. Example
pytest --log-cli-level=ERROR snippets/test_basics.tavern.yaml
Can also use pytest.ini to set logging level:
log_cli = 1
log_level = INFO
log_cli_level = INFO
Example of pytest.ini
Here is what I have in my pytest.ini file
[pytest]
tavern-global-cfg=
snippets/common_snippets.yaml
tests/common.yaml
tests/secrets.yaml
snippets/api_urls.yaml
tavern-strict=json:off
tavern-beta-new-traceback = True
filterwarnings =
ignore::UserWarning
ignore::ImportWarning
ignore::ResourceWarning
testpaths = tests, snippets
addopts =
--doctest-modules
-r xs
-p no:warnings
-vv
--tb=short
log_cli = 1
log_level = INFO
log_cli_level = INFO
log_cli_format = %(asctime)s %(levelname)s %(message)s
log_cli_date_format = %H:%M:%S
Test Examples
Simple request and validate status code:
---
test_name: Sending request and checking response status code ex01
stages:
- name: Check that HTTP status code equals 200
request:
url: http://api.zippopotam.us/us/90210
method: GET
response:
status_code: 200
Request variables
---
test_name: Using request variables ex02
stages:
- name: Create a resource, verify response json using request variables
request:
url: https://jsonplaceholder.typicode.com/posts
method: POST
json:
title: foo
body: bar
userId: 1
headers:
content-type: application/json; charset=UTF-8
response:
status_code:
- 201
# - 503
json:
title: "{tavern.request_vars.json.title}"
body: "{tavern.request_vars.json.body}"
userId: !int "{tavern.request_vars.json.userId}"
id: 101
Environment variables. Before running the test update ~/.bash_profile:
---
# export TITLE="foo"
# export PYTHONPATH="$PYTHONPATH:/Users/maksim/repos/tau-tools-demo/py-tavern-api/tests"
# source ~/.bash_profile
test_name: Using environment variables ex03
stages:
- name: Create a resource, use environment variables to validate response
request:
url: https://jsonplaceholder.typicode.com/posts
method: POST
json:
title: foo
body: bar
userId: 1
headers:
content-type: application/json; charset=UTF-8
response:
status_code: 201
json:
title: "{tavern.env_vars.TITLE}"
Checking the response using external functions. Note: you can only use an external function to create the whole body or response – #if you just want one value, you should use a pytest fixture (an example is in the documentation).
Before running the test update~/.bash_profile. Example:
# export PYTHONPATH="Users/maksim/repos/tau-tools-demo/py-tavern-api/tests"
# to avoid affecting other projects after you finished work in Tavern project comment out/delete the above line and run:
# unset PYTHONPATH ; source ~/.bash_profile
#
test_name: Using external function to validate response ex04
stages:
- name: Make sure we have the right ID
request:
url: https://jsonplaceholder.typicode.com/posts/1
method: GET
response:
status_code: 200
verify_response_with:
function: utils:get_id
Here is get_id function:
# conftest.py
def get_id(response):
# Make sure that id=1 in the response
assert response.json().get("id") == 1
assert response.json().get("userId") == 1
Using external function with extra arguments to validate response:
---
test_name: Using external function with extra arguments to validate response ex04a
stages:
- name: Make sure we have the right ID
request:
url: https://jsonplaceholder.typicode.com/posts/1
method: GET
response:
status_code: 200
verify_response_with:
function: utils:demo_extra_kwargs
extra_kwargs:
arg1: "hello"
arg2: "world"
Here is demo_extra_kwargs function:
def demo_extra_kwargs(response, arg1, arg2):
# Make sure that arg1 received
logging.info(f'\nArg1: {arg1}')
logging.info(f'\nArg2: {arg2}')
Using built-in validators:
---
test_name: Using built-in validators ex05
stages:
- name: Make sure the response matches the given schema
request:
url: https://jsonplaceholder.typicode.com/posts/1
method: GET
response:
status_code: 200
verify_response_with:
function: tavern.testutils.helpers:validate_pykwalify
extra_kwargs:
schema:
type: map
mapping:
id:
type: int
required: True
title:
type: any
required: True
body:
type: any
required: True
userId:
type: any
required: True
# Expected
#{
# id: 1,
# title: '[...]',
# body: '[...]',
# userId: 1
#}
Using built-in validator. The dict can have keys which are not present in the schema, and these can map to anything. Usage: allowempty: True
See documentation here https://pykwalify.readthedocs.io/en/unstable/validation-rules.html#mapping
---
test_name: Using built-in validators and allowempty ex06
stages:
- name: Make sure the response matches the given schema
request:
url: https://jsonplaceholder.typicode.com/posts/1
method: GET
response:
status_code: 200
verify_response_with:
function: tavern.testutils.helpers:validate_pykwalify
extra_kwargs:
schema:
type: map
allowempty: True
mapping:
id:
type: int
required: True
title:
type: any
required: True
Simple request and validate headers:
---
test_name: Get location for US zip code 90210 and validate response content type is equal to ‘application/json’ ex07
strict:
- headers:off
- json:off
stages:
- name: Check that HTTP status code equals 200 and other fields
request:
url: http://api.zippopotam.us/us/90210
method: GET
response:
headers:
content-type: application/json
Simple request and validate json content:
---
#
test_name: Get location for US zip code 90210 and check response body content ex08
stages:
- name: Check that place name equals Beverly Hills
request:
url: http://api.zippopotam.us/us/90210
method: GET
response:
json:
places:
- place name: Beverly Hills
How to use parametrization:
test_name: Using parametrization in test ex09
marks:
- parametrize:
key:
- country_code
- zip_code
- place_name
vals:
- [us, 12345, Schenectady]
- [ca, B2A, North Sydney South Central]
- [nl, 3825, Vathorst]
stages:
- name: Verify place name in response body
request:
url: http://api.zippopotam.us/{country_code}/{zip_code}
method: GET
response:
json:
places:
- place name: "{place_name}"
Using external functions:
---
test_name: Injecting external data into a request using external functions for other things ex10
stages:
- name: Injecting external data into a request
request:
url: http://www.recipepuppy.com/api/
method: GET
json:
$ext:
function: utils:generate_req1
response:
status_code: 200
json:
title: Recipe Puppy
version: 0.1
results:
- title: "Ginger Champagne"
ingredients: "champagne, ginger, ice, vodka"
Here is external function generate_req1:
# tests/utils.py
from box import Box
def generate_req1():
req = {
"i": "avocado",
"q": "kale",
"p": 1,
}
return Box(req)
Saving data from response using external functions. In this case, both `{test_title}` and `{test_ingredients}` are available for use in later requests:
---
test_name: Saving data from response using external functions ex11
stages:
- name: Validate status code 200
request:
url: http://www.recipepuppy.com/api/
method: GET
json:
i: avocado
q: kale
p: 1
response:
json:
title: "Recipe Puppy"
save:
$ext:
function: utils:save_data
json:
test_ingredients: results[0].ingredients
- name: Validate saved data
request:
url: http://www.recipepuppy.com/api/
method: GET
json:
i: avocado
q: kale
p: 1
response:
status_code: 200
json:
results:
- title: "{test_title}"
ingredients: "{test_ingredients}"
Here is save_data function:
# tests/utils.py
from box import Box
def save_data(response):
return Box({"test_title": response.json()["results"][0]["title"]})
Reusing requests and YAML fragments:
---
test_name: Reusing requests and YAML fragments part1 ex12
stages:
- name: &name_block Send request
request: &request_block
url: http://www.recipepuppy.com/api/?i=avocado&q=kale&p=1
method: GET
---
test_name: Reusing requests and YAML fragments part2 ex13
stages:
- name: Reusing req block
request:
*request_block
response:
status_code: 200
json:
title: Recipe Puppy
version: 0.1
results:
- title: "A Ok Salad Recipe"
ingredients: "cayenne, lemon, avocado, kale, sea salt, tomato"
Including external configuration files, using common.yaml, includes.yaml should be in the same folder as the tests
---
test_name: Including external files ex15
includes:
- !include includes.yaml
stages:
- name: Check that HTTP status code equals 200
request:
url: "{protocol1}://{host_gorest}{res_users}"
method: GET
response:
status_code: 200
json:
result:
name: Unauthorized
code: 0
status: 401
# tests/includes.yaml
---
name: Common test information
description: Some information for tests
# Variables should just be a mapping of key: value pairs
variables:
protocol: https
host_gorest: gorest.co.in
res_users: /public-api/users
# tests/common.yaml
---
name: Common test information
description: Some information for tests
# Variables should just be a mapping of key: value pairs
variables:
protocol1: https
host_gorest: gorest.co.in
res_users: /public-api/users
protocol2: http
host_rp: www.recipepuppy.com
res_rp: /api/
host_zipo: api.zippopotam.us
api-arg: !raw '{"path": "/test_pdf.pdf", "mode": "add","autorename": true, "mute": false, "strict_conflict": false}'
# this is path in your Dropbox folder. In this case file will be uploaded to the root folder
stages:
- id: add_rand_user
name: Authenticate and add new random user
request:
url: https://gorest.co.in/public-api/users
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302 # the requested resource has been temporarily moved to a different URI
json:
_meta:
message: "A resource was successfully created in response to a POST request. The Location header contains the URL pointing to the newly created resource."
result:
id: !anystr
save:
json:
user_id: result.id
- id: update_user_rand
name: Update user
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: PUT
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302 # the requested resource has been temporarily moved to a different URI
json:
_meta:
message: "OK. Everything worked as expected."
result:
id: !anystr
save:
json:
user_id_upd: result.id
- id: delete_user
name: Delete user
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: DELETE
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
json:
_meta:
code: 204
message: "The request was handled successfully and the response contains no body content."
Also pytest.ini can be used to include external files:
# [pytest]
# tavern-global-cfg=tests/common.yaml
# tavern-global-cfg=tests/includesA.yaml
# tavern-global-cfg=tests/includesB.yaml
Including global configuration files:
---
test_name: Including global configuration files part1 ex16
includes:
- !include includesA.yaml
stages:
- name: Send request
request:
url: "{protocol2}://{host_rp}{res_rp}{query1}"
method: GET
response:
status_code: 200
json:
results:
- title: A Ok Salad Recipe
---
test_name: Including global configuration files part2 ex17
includes:
- !include includesB.yaml
stages:
- name: Send request
request:
url: "{protocol2}://{host_rp}{res_rp}{query2}"
method: GET
response:
status_code: 200
json:
results:
- title: Chocolate-chocolate Chip Banana Muffins Recipe
- title: Chocolate Banana Chocolate Chip Bundt Cake
- title: Banana Fritters in Chocolate Batter And Chocolate Sauce
Authentication example. Using saved variable. Using external function to generate request you can only use an external function to create the whole body or response – if you just want one value, you should use a pytest fixture (an example is in the documentation).
Note that there is also no way to use a fixture to generate a block of json – either use $ext to generate the whole
request, or a fixture to generate one value. Anything in between is currently not implemented (see issue#191 in github).
---
test_name: Authenticate and add new random user ex19
stages:
- name: Authenticate and add new random user
request:
url: https://gorest.co.in/public-api/users
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302
json:
result:
id: !anystr
save:
json:
user_id: result.id
- name: Check user's details
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: GET
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
verify_response_with:
function: utils:log_response
json:
result:
first_name: Max
gender: male
id: "{user_id}"
Sharing stages in configuration files:
---
test_name: Sharing stages in configuration ex20
stages:
- type: ref
id: add_rand_user
- name: Check user's details
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: GET
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
verify_response_with:
function: utils:log_response
json:
result:
first_name: Max
gender: male
id: "{user_id}"
Here is stage defined in common.yaml:
# tests/common.yaml
stages:
- id: add_rand_user
name: Authenticate and add new random user
request:
url: https://gorest.co.in/public-api/users
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302 # the requested resource has been temporarily moved to a different URI
json:
_meta:
message: "A resource was successfully created in response to a POST request. The Location header contains the URL pointing to the newly created resource."
result:
id: !anystr
save:
json:
user_id: result.id
Matching arbitrary return values in a response:;
---
test_name: Match arbitrary return values ex21
stages:
- name: Check value types in user details
request:
url: https://gorest.co.in/public-api/users
method: GET
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
json:
result:
- first_name: !anystr
status: active
id: !anystr
Matching via a regular expression:
---
test_name: Matching via a regular expression ex22
stages:
- type: ref
id: add_rand_user
- name: Check value types in user details
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: GET
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
json:
result:
# - first_name: !re_fullmatch "[A-Za-z0-9]"
id: !re_search "[0-9]"
first_name: !re_search "[A-Za-z0-9]"
email: !re_search "[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+[a-zA-Z0-9-.]"
status: active
Adding a delay between tests:
---
test_name: Adding a delay between tests ex23
stages:
- type: ref
id: add_rand_user
- type: ref
id: update_user_rand
- name: Check user's details
delay_before: 4
request:
url: https://gorest.co.in/public-api/users/{user_id_upd}
method: GET
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
response:
status_code: 200
json:
result:
first_name: Max
gender: male
id: "{user_id_upd}"
add_rand_user and update_user_rand stages are defined in common.yaml:
#common.yaml
stages:
- id: add_rand_user
name: Authenticate and add new random user
request:
url: https://gorest.co.in/public-api/users
method: POST
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302 # the requested resource has been temporarily moved to a different URI
json:
_meta:
message: "A resource was successfully created in response to a POST request. The Location header contains the URL pointing to the newly created resource."
result:
id: !anystr
save:
json:
user_id: result.id
- id: update_user_rand
name: Update user
request:
url: https://gorest.co.in/public-api/users/{user_id}
method: PUT
headers:
Content-Type: application/json
Authorization: "Bearer {tavern.env_vars.API_KEY_GOREST}"
# replace above variable with your API_KEY
# go to https://gorest.co.in/user/login.html to register free account
json:
$ext:
function: utils:generate_req_rand
response:
status_code:
- 200
- 302 # the requested resource has been temporarily moved to a different URI
json:
_meta:
message: "OK. Everything worked as expected."
result:
id: !anystr
save:
json:
user_id_upd: result.id
Retrying tests:
---
test_name: Retrying tests ex24
stages:
- name: Create a resource
max_retries: 2 # retry a stage 2 times
request:
url: https://jsonplaceholder.typicode.com/posts
method: POST
json:
title: foo
body: bar
userId: 2
headers:
content-type: application/json; charset=UTF-8
response:
status_code: 201
# Marking tests. Make sure pytest.ini does not contain ” addopts = –strict” otherwise add markers names:
---
test_name: Marking test ex25
marks:
- gorest
stages:
- type: ref
id: add_rand_user
- type: ref
id: delete_user
Skipping a test:
---
test_name: Skipping a test ex26
marks:
- skip
stages:
- name: Check that HTTP status code equals 200 and other fields
request:
url: http://api.zippopotam.us/us/90211
method: GET
response:
headers:
content-type: application/json
---
test_name: Skipping a test using skipif ex27
marks:
- skipif: "'api' in '{host_zipo}'"
stages:
- name: Check that HTTP status code equals 200 and other fields
request:
url: "http://{host_zipo}/us/90211"
method: GET
response:
headers:
content-type: application/json
Using pytest fixtures:
---
test_name: Using fixtures ex29
marks:
- usefixtures:
- zipcode # fixture is defined in conftest.py
stages:
- name: Read zip code from file and use in request
request:
url: "http://{host_zipo}/us/{zipcode}"
method: GET
response:
headers:
content-type: application/json
status_code: 200
Fixture is defined in conftest.py:
#conftest.py
import logging
import os
import html
import yaml
@pytest.fixture
def zipcode():
current_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(current_dir, "zip_code.yaml"), "r") as file:
zcode = yaml.load(file, Loader=yaml.FullLoader)
file.close()
logging.info(f'Code: {zcode["code"]}')
return zcode["code"]
Using hooks:
---
test_name: Using hook pytest_tavern_beta_after_every_response ex30
# hook is defined in conftest.py and logs out response
marks:
- usefixtures:
- zipcode # fixture defined in conftest.py
stages:
- name: Read zip code from file and use in request
request:
url: "http://{host_zipo}/us/{zipcode}"
method: GET
response:
headers:
content-type: application/json
Here is hook pytest_tavern_beta_after_every_response defined in conftest.py:
def pytest_tavern_beta_after_every_response(expected, response):
try:
logging.info(f"================= RESPONSE ================== "
f"\n\nstatus code [{response.status_code}]\n{dumps(response.json(), indent=4)}\n\n")
except ValueError as e:
if "!DOCTYPE html" in response.text:
logging.info(f"================= RESPONSE ================== "
f"\n\nstatus code [{response.status_code}]\n{html.unescape(response.text)}\n\n")
else:
logging.info(f"================= RESPONSE ================== "
f"\n\nstatus code [{response.status_code}]\n{response.text,}\n\n")
return
Authenticate and get current Dropbox account:
test_name: Authenticate and get current Dropbox account info ex31
stages:
- name: Authenticate and check that HTTP status code equals 200
request:
url: https://api.dropboxapi.com/2/users/get_current_account
method: POST
headers:
Authorization: "Bearer {tavern.env_vars.API_KEY_DROPBOX}"
# replace the above variable with your API_KEY
# go to https://www.dropbox.com/developers/apps > create app > Dropbox API > Full dropbox > appname > create app
response:
status_code: 200
json:
account_id: !anystr
name:
display_name: !anystr
Get the contents of Dropbox root folder:
---
# Simple request and validate status code
test_name: Get the contents of Dropbox root folder ex32
stages:
- name: Authenticate and get the contents of Dropbox root folder
request:
url: https://api.dropboxapi.com/2/files/list_folder
method: POST
headers:
Authorization: "Bearer {tavern.env_vars.API_KEY_DROPBOX}"
# replace the above variable with your API_KEY
# go to https://www.dropbox.com/developers/apps > create app > Dropbox API > Full dropbox > appname > create app
Content-Type: application/json
json:
path: ""
recursive: false
include_media_info: false
include_deleted: false
include_has_explicit_shared_members: false
include_mounted_folders: true
include_non_downloadable_files: true
response:
status_code: 200
json:
entries: !anylist
Upload file using Dropbox API. Uploaded file located in test_data/test_pdf.pdf:
---
test_name: Upload file to Dropbox folder (file body) ex33
stages:
- name: Upload file to Dropbox folder
request:
url: https://content.dropboxapi.com/2/files/upload
method: POST
file_body: "test_data/test_pdf.pdf"
headers:
Authorization: "Bearer {tavern.env_vars.API_KEY_DROPBOX}" # replace the above variable with your API_KEY
Content-Type: application/octet-stream
Dropbox-API-Arg: "{api-arg}"
response:
status_code: 200
#tests/common.yaml
api-arg: !raw '{"path": "/test_pdf.pdf", "mode": "add","autorename": true, "mute": false, "strict_conflict": false}'
# this is path in your Dropbox folder. In this case file will be uploaded to the root folder