python-3.x - 对处理 csv 文件的函数进行单元测试的最佳方法是什么?

标签 python-3.x pandas unit-testing testing pytest

我正在尝试使用 Pytest 对处理 csv 文件的函数进行单元测试。虽然我的功能有效,但我觉得在我的项目目录中创建“示例”csv 文件以测试功能时有很多代码重复。保存真实数据的实际 csv 文件有数百万条记录。

这些不是我必须在我的模块中测试的唯一 csv 文件,因此了解什么是测试适用于不同文件结构的函数的最佳方法将非常有帮助。

现在,我正在创建一个非常短的 csv 文件,该文件使用单行数据和通过函数处理文件后的预期数据帧输出来模拟实际文件架构。

也许 mock 是可行的方法?但我觉得您不需要为这种测试进行模拟

测试函数

@pytest.mark.parametrize('test_file, expected', [
    (r'Path\To\Project\Output\Folder\mock_sales1.csv',
     pd.DataFrame([['A0A0A0', 1, 4000]], columns=['Postal_Code', 'Store_Num', 'Sales'])),
    (r'Path\To\Project\Output\Folder\mock_sales2.csv',
     pd.DataFrame([['A0A0A0', 1, 4000]], columns=['Postal_Code', 'Store_Num', 'Sales']))
])
def test_sales_dataframe(test_file, expected):
    # This part is repetitive, different tests each need a seperate file written within the test function.
    # Writing sample file to test that files with 7 columns are read correctly.
    mock_mks_sales1 = [['Data0', 'A0A0A0', 1, 'Data3', 'Data4', 'Data5', 4000]]
    with open(r'Path\To\Project\Output\Folder\mock_sales1.csv', 'w') as file:
        writer = csv.writer(file)
        writer.writerows(mock_sales1)
    # Writing sample file to test that files with 8 columns are read correctly.
    mock_mks_sales2 = [['Data0', 'A0A0A0', 1, 'Data3', 'Data4', 'Data5', 'Data6', 4000]]
    with open(r'Path\To\Project\Output\Folder\mock_sales2.csv', 'w') as file:
        writer = csv.writer(file)
        writer.writerows(mock_sales2)

    sales_df = mks_sales_dataframe(test_file)
    testing.assert_frame_equal(expected, sales_df)

    os.remove(r'Path\To\Project\Output\Folder\mock_sales1.csv')
    os.remove(r'Path\To\Project\Output\Folder\mock_sales2.csv')

主要功能

def sales_dataframe(file):
    try:
        with open(file, 'r') as f:
            reader = csv.reader(f)
            num_cols = len(next(reader))
            columns = [1, 2, (num_cols - 1)]  # Number of columns is variable, this is used later to accurately specify which columns should be read. This is part I'm testing!

        sales_df = pd.read_csv(file, usecols=columns, names=['Postal_Code', 'Store_Num', 'Sales'])
        return sales_df
    except FileNotFoundError:
        raise FileNotFoundError(file)

测试按预期通过。但是,对于每个不同的测试,我都必须在测试函数中创建一个示例 csv 文件,并在测试完成后删除每个文件。正如您可以想象的那样,在单个测试函数中有很多重复代码,感觉非常笨拙和冗长,尤其是当测试被参数化时。

最佳答案

我认为问题在于您的测试输入和预期输出紧密相关,但位于两个不同的位置,一个在参数中,另一个在测试代码中。
如果更改一个参数,除了重复的代码之外,您还需要更改测试的方法体,这在我看来是不正确的。

我认为你应该有参数 test(test_data, expected output) 并将输入注入(inject)一个临时文件。
然后调用您的函数并比较预期输出和实际输出。

@pytest.mark.parametrize('test_data, expected', [
    ([['Data0', 'A0A0A0', 1, 'Data3', 'Data4', 'Data5', 4000]],
      pd.DataFrame([['A0A0A0', 1, 4000]], columns=['Postal_Code', 'Store_Num', 'Sales'])),
    ([['Data0', 'A0A0A0', 1, 'Data3', 'Data4', 'Data5', 'Data6', 4000]],
      pd.DataFrame([['A0A0A0', 1, 4000]], columns=['Postal_Code', 'Store_Num', 'Sales']))
])
def test_sales_dataframe(test_data, expected):

    # Write your test data in a temporary file
    tmp_file = r'Path\To\Project\Output\Folder\tmp.csv';
    with open(tmp_file, 'w') as file:
        writer = csv.writer(file)
        writer.writerows(test_data)

    # Process the data
    sales_df = mks_sales_dataframe(tmp_file)

    # Compare expected and actual output
    testing.assert_frame_equal(expected, sales_df)

    # Clean the temporary file
    os.remove(tmp_file)

您也可以创建您的 .csv 并将它们添加为测试资源,但是您的输入和预期输出将有不同的位置,这不是很好。

关于python-3.x - 对处理 csv 文件的函数进行单元测试的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57097257/

相关文章:

python - 如何将 Pandas 数据框中的列拆分为字母值和数值?

python - 推广小部件并从生成的 ui 中使用它们

java - 使用 mockito 对 DAO 层进行单元测试

python - 如何模拟 boto3 的 StreamingBody 对象以在 Python 中使用 BytesIO 进行处理?

python - 运行 google_sql 我收到无效语法错误

python - 如果嵌套字典中存在值,则查找键

python - 从 pickle 读取时,数据帧被解析为元组

python - Pandas:将 WinZipped csv 文件转换为 Data Frame

python - 更改 x 轴而不更改 pandas 中的索引

python - 单元测试 MySQL 数据库交互