如何在 python 中使用 pytest 对 json 数组测试数据进行参数化测试?
How to parametrize tests with json array test data using pytest in python?
我有一些测试用例和测试数据,其中测试数据采用 JSON 数组形式,如下所示:
{
"valid_data": [
{
"id": "1234",
"name": "John"
},
{
"id": "2234",
"name": "Mary"
},
{
"id": "3234",
"name": "Kenny"
},
],
"invalid_data": [
{
"id": "1234",
"name": "Mary"
},
{
"id": "2234",
"name": "Kenny"
},
{
"id": "3234",
"name": "John"
},
]
}
我现在正在测试一个 API,它将接受 JSON 作为输入并以状态代码响应。如果 id 和 name 在数据库中匹配,它将响应 200 OK。否则 400 错误请求。
这是我当前的测试用例:
def get_test_data(filename):
folder_path = os.path.abspath(Path(os.path.dirname(__file__)))
folder = os.path.join(folder_path, 'TestData')
jsonfile = os.path.join(folder, filename)
with open(jsonfile) as file:
data = json.load(file)
return data
def test_valid_ok(database_api):
data = get_test_data('test_data.json')
response = database_api.get_user_info(data)
assert requests.codes['ok'] == response.status_code
在我的测试中,我只是声明了方法 database_api
并将 URL 作为参数,然后它将向 api 发送一个 post 请求。对于这部分没有问题,我已经测试过它工作正常。
但是根据当前的结构和代码,我只能在 json 文件中包含 1 json 数据。我想要一个数据驱动的测试,它能够根据我在 json 文件中的测试数据 运行 多次。
我已经查看了 pytest 官方文档和各种建议使用 pytest 参数化函数的在线资源,但我无法正确使用 json 文件。
谢谢大家帮忙!
您可以使用 pytest_generate_tests 挂钩对动态数据进行参数化。
首先创建一个fixture,可以被测试函数调用获取测试数据
# in conftest.py
@pytest.fixture()
def test_data(request):
return request.param
现在您可以对其进行参数化,以便为每个测试数据集调用它:
#in conftest.py
def pytest_generate_tests(metafunc):
testdata = get_test_data('test_data.json')
metafunc.parametrize('test_data', testdata, indirect=True)
传递给parametrize
函数的testdata
必须是一个列表。所以你需要在传递输入数据之前稍微修改一下。我稍微修改了 get_test_data
函数。
#in conftest.py
def get_test_data(filename):
folder_path = os.path.abspath(os.path.dirname(__file__))
folder = os.path.join(folder_path, 'TestData')
jsonfile = os.path.join(folder, filename)
with open(jsonfile) as file:
data = json.load(file)
valid_data = [(item, 1) for item in data['valid_data']]
invalid_data = [(item, 0) for item in data['invalid_data']]
# data below is a list of tuples, with first element in the tuple being the
# arguments for the API call and second element specifies if this is a test
# from valid_data set or invalid_data. This can be used for putting in the
# appropriate assert statements for the API response.
data = valid_data + invalid_data
return data
现在您的测试函数可能如下所示:
#in test_API.py
def test_(test_data):
response = database_api.get_user_info(test_data[0])
# Add appropriate asserts.
# test_data[1] == 1 means 200 response should have been received
# test_data[1] == 0 means 400 response should have been received
我刚刚写了一个名为 parametrize_from_file
的包来解决这个问题。以下是此示例的工作方式:
import parametrize_from_file
# If the JSON file has the same base name as the test module (e.g. "test_api.py"
# and "test_api.json"), this parameter isn't needed.
path_to_json_file = ...
@parametrize_from_file(path_to_json_file, 'valid_data')
def test_valid_data(id, name):
request = dict(id=id, name=name)
response = database_api.get_user_info(request)
assert response.status_code == 200
@parametrize_from_file(path_to_json_file, 'invalid_data')
def test_invalid_data(id, name):
request = dict(id=id, name=name)
response = database_api.get_user_info(request)
assert response.status_code == 400
您可以通过稍微重组 JSON 文件来稍微简化此代码:
# test_api.json
{
"test_id_name_requests": [
{
"request": {
"id": "1234",
"name": "John"
},
"status_code": 200
},
{
"request": {
"id": "2234",
"name": "Mary"
},
"status_code": 200
},
{
"request": {
"id": "3234",
"name": "Kenny"
},
"status_code": 200
},
{
"request": {
"id": "1234",
"name": "Mary"
},
"status_code": 400
},
{
"request": {
"id": "2234",
"name": "Kenny"
},
"status_code": 400
},
{
"request": {
"id": "3234",
"name": "John"
},
"status_code": 400
},
],
}
对于这个文件,只需要一个测试函数,不需要给 @parametrize_from_file
装饰器任何参数:
# test_api.py
import parametrize_from_file
@parametrize_from_file
def test_id_name_requests(request, status_code):
response = database_api.get_user_info(request)
assert response.status_code == status_code
我有一些测试用例和测试数据,其中测试数据采用 JSON 数组形式,如下所示:
{
"valid_data": [
{
"id": "1234",
"name": "John"
},
{
"id": "2234",
"name": "Mary"
},
{
"id": "3234",
"name": "Kenny"
},
],
"invalid_data": [
{
"id": "1234",
"name": "Mary"
},
{
"id": "2234",
"name": "Kenny"
},
{
"id": "3234",
"name": "John"
},
]
}
我现在正在测试一个 API,它将接受 JSON 作为输入并以状态代码响应。如果 id 和 name 在数据库中匹配,它将响应 200 OK。否则 400 错误请求。
这是我当前的测试用例:
def get_test_data(filename):
folder_path = os.path.abspath(Path(os.path.dirname(__file__)))
folder = os.path.join(folder_path, 'TestData')
jsonfile = os.path.join(folder, filename)
with open(jsonfile) as file:
data = json.load(file)
return data
def test_valid_ok(database_api):
data = get_test_data('test_data.json')
response = database_api.get_user_info(data)
assert requests.codes['ok'] == response.status_code
在我的测试中,我只是声明了方法 database_api
并将 URL 作为参数,然后它将向 api 发送一个 post 请求。对于这部分没有问题,我已经测试过它工作正常。
但是根据当前的结构和代码,我只能在 json 文件中包含 1 json 数据。我想要一个数据驱动的测试,它能够根据我在 json 文件中的测试数据 运行 多次。
我已经查看了 pytest 官方文档和各种建议使用 pytest 参数化函数的在线资源,但我无法正确使用 json 文件。
谢谢大家帮忙!
您可以使用 pytest_generate_tests 挂钩对动态数据进行参数化。
首先创建一个fixture,可以被测试函数调用获取测试数据
# in conftest.py
@pytest.fixture()
def test_data(request):
return request.param
现在您可以对其进行参数化,以便为每个测试数据集调用它:
#in conftest.py
def pytest_generate_tests(metafunc):
testdata = get_test_data('test_data.json')
metafunc.parametrize('test_data', testdata, indirect=True)
传递给parametrize
函数的testdata
必须是一个列表。所以你需要在传递输入数据之前稍微修改一下。我稍微修改了 get_test_data
函数。
#in conftest.py
def get_test_data(filename):
folder_path = os.path.abspath(os.path.dirname(__file__))
folder = os.path.join(folder_path, 'TestData')
jsonfile = os.path.join(folder, filename)
with open(jsonfile) as file:
data = json.load(file)
valid_data = [(item, 1) for item in data['valid_data']]
invalid_data = [(item, 0) for item in data['invalid_data']]
# data below is a list of tuples, with first element in the tuple being the
# arguments for the API call and second element specifies if this is a test
# from valid_data set or invalid_data. This can be used for putting in the
# appropriate assert statements for the API response.
data = valid_data + invalid_data
return data
现在您的测试函数可能如下所示:
#in test_API.py
def test_(test_data):
response = database_api.get_user_info(test_data[0])
# Add appropriate asserts.
# test_data[1] == 1 means 200 response should have been received
# test_data[1] == 0 means 400 response should have been received
我刚刚写了一个名为 parametrize_from_file
的包来解决这个问题。以下是此示例的工作方式:
import parametrize_from_file
# If the JSON file has the same base name as the test module (e.g. "test_api.py"
# and "test_api.json"), this parameter isn't needed.
path_to_json_file = ...
@parametrize_from_file(path_to_json_file, 'valid_data')
def test_valid_data(id, name):
request = dict(id=id, name=name)
response = database_api.get_user_info(request)
assert response.status_code == 200
@parametrize_from_file(path_to_json_file, 'invalid_data')
def test_invalid_data(id, name):
request = dict(id=id, name=name)
response = database_api.get_user_info(request)
assert response.status_code == 400
您可以通过稍微重组 JSON 文件来稍微简化此代码:
# test_api.json
{
"test_id_name_requests": [
{
"request": {
"id": "1234",
"name": "John"
},
"status_code": 200
},
{
"request": {
"id": "2234",
"name": "Mary"
},
"status_code": 200
},
{
"request": {
"id": "3234",
"name": "Kenny"
},
"status_code": 200
},
{
"request": {
"id": "1234",
"name": "Mary"
},
"status_code": 400
},
{
"request": {
"id": "2234",
"name": "Kenny"
},
"status_code": 400
},
{
"request": {
"id": "3234",
"name": "John"
},
"status_code": 400
},
],
}
对于这个文件,只需要一个测试函数,不需要给 @parametrize_from_file
装饰器任何参数:
# test_api.py
import parametrize_from_file
@parametrize_from_file
def test_id_name_requests(request, status_code):
response = database_api.get_user_info(request)
assert response.status_code == status_code