Tavern Rest API Testing Examples

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:

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:

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

Leave a Reply

Your email address will not be published. Required fields are marked *