How to read and send CSV column data to PyTest test cases
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:
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
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 adefaultdict
to return the same test cases for any test name.