1

We have a utility that calls APIs and saves their responses to CSV. That CSV (resp.csv) has API request (column A) along with headers (column C) and payload (column B) in it. And also the body of their response is stored in column D, and the response code in column E (not visible in the CSV image).

The CSV file looks like this:

enter image description here

I want to pass each response to a set of PyTest test cases which will be having some assertion specific to the response.

For checking the status code I can do this via a function call, which returns the response code before writing to CSV. But the requirement is to read responses from CSV and pass them for assertion/test cases:

@pytest.mark.parametrize('test_input', check_response)
def test_APIResponse(test_input):
    print(check_response)
    assert test_input == 200, "Test pass"

How can I call the response body stored at CSV (column D) and do assertion via using PyTest test cases?

Can someone guide me with this?

Thanks

accdias
  • 5,160
  • 3
  • 19
  • 31
Finch
  • 71
  • 1
  • 9
  • where are you actually calling the API? you don't seem to be calling any actual function – gold_cy Jan 13 '22 at 12:36
  • Please, avoid [posting images of text](https://unix.meta.stackexchange.com/questions/4086/psa-please-dont-post-images-of-text). It is a better practice to transcribe them instead. – accdias Jan 13 '22 at 12:42
  • https://stackoverflow.com/users/6789321/accdias While creating table text editor was giving error while posting the question. I will try again. – Finch Jan 13 '22 at 12:46
  • https://stackoverflow.com/users/6817835/gold-cy updated the question. – Finch Jan 13 '22 at 12:47

2 Answers2

1

I wrote a package called Parametrize From File that can be used to do this. I gave a detailed example of how to load test parameters from an XLSX file in another Stack Overflow post, but I'll briefly reiterate the important points here.

The only complication is that Parametrize From File expects to be able to load test cases as a dictionary of lists of dictionaries (see the docs for more info). This layout makes sense for YAML/TOML/NestedText files, but not for XLSX/CSV files. So we need to provide a function that loads the XSLX/CSV file in question and converts it to the expected format. pandas makes this pretty easy to do if you're willing to add the dependency, otherwise it probably wouldn't be too hard to write something yourself.


Edit: Here's a more concrete example. To begin, here's what the CSV file might look like:

request_,payload,header,response,code
http://localhost:8080/c/u/t,{"ci":""},{},{"ss":""},200
http://localhost:8080/c/u/t?Id=x,{"ci":""},{},{"res":""},200

A few things to note:

  • The first row gives a name to each column. The code I've written relies on this, and use those same names as the arguments to the parametrized test function. If your file doesn't have these headers, you would need to hard-code names each column.
  • The name "request" is reserved by pytest, so we have to use "request_" here.

Here's what the corresponding test script might look like:

import parametrize_from_file as pff
from csv import DictReader
from collections import defaultdict

def load_csv(path):
    with open(path) as f:
        cases = list(DictReader(f))
    return defaultdict(lambda: cases)

pff.add_loader('.csv', load_csv)

@pff.parametrize
def test_api_request_response(request_, payload, header, response, code):
    assert request_ == ...
    assert payload == ...
    assert headers == ...
    assert response == ...
    assert code == ...

A few things to note:

  • This assumes that the CSV file has the same base name as the test script. If this isn't the case, it's easy to specify a different path.
  • The load function is expected to return a dictionary mapping test names (e.g. test_api_request_response) to lists of test cases, where each test case is a dictionary mapping parameter names (e.g. request_) to parameter values (e.g. http://localhost:8080). In this case the files doesn't specify any test names, so we cheat and use a defaultdict to return the same test cases for any test name.
Kale Kundert
  • 1,144
  • 6
  • 18
  • @https://stackoverflow.com/users/2066725/kale-kundert I am bit confused over this. As mentioned in comment, converted this CSV to dict object. Now my dict1 looks like: `{'column A1 value': ['{column b1}',"{column c1}",'{column d1}','column e1'],'column A2 value': ['{column b2}',"{column c2}",'{column d2}','column e2']...}` If possible could you please guide me how to pass this dict, specifically column d and e for assertion using PyTest? In column D there is xml response stored and I'd we doing schema checks and contains test cases. Thanks in advance Kale. Cheers. – Finch Jan 17 '22 at 16:34
1

Check this out, I think it might help docs. For your specific example you could de something like

import pytest


def load_cases():
    # read_csv
    class Case:
        def __init__(self, number):
            self.i = number

        def __repr__(self):
            return '{number}'.format(number=self.i)

    return [Case(i) for i in range(5)]


def parametrize(name, values):
    # function for readable description
    return pytest.mark.parametrize(name, values, ids=map(repr, values))


@parametrize("case", load_cases())
def test_responses(case):
    print(case.i)

You are creating class Case and store everything you need inside this class then access properties from test. You also can play around with indirect parametrization for fixtures, but don't overcomplicate your code.

To read a specific column use something like pandas or just split your string.

Ilya
  • 45
  • 1
  • 10