Python 如何测试更复杂的函数?

Python 如何测试更复杂的函数?,python,pandas,testing,pytest,Python,Pandas,Testing,Pytest,我是一个完全的业余/业余开发人员,试图学习更多关于测试我编写的软件的知识。虽然我理解测试的核心概念,但随着功能变得越来越复杂,我觉得它就像一个充满变化、结果、条件等的兔子洞 下面的函数将文件从目录读入数据帧。在将数据传递到另一个函数(最终将数据导入数据库)之前,需要进行几列调整 我已经为convert\u date\u string函数编写了一个测试。但是这个函数作为一个整体如何呢?我如何为它编写一个测试呢?在我看来,Pandas库的大部分内容已经过测试——因此确保那里的核心功能与我的设置一起工

我是一个完全的业余/业余开发人员,试图学习更多关于测试我编写的软件的知识。虽然我理解测试的核心概念,但随着功能变得越来越复杂,我觉得它就像一个充满变化、结果、条件等的兔子洞

下面的函数将文件从目录读入数据帧。在将数据传递到另一个函数(最终将数据导入数据库)之前,需要进行几列调整

我已经为
convert\u date\u string
函数编写了一个测试。但是这个函数作为一个整体如何呢?我如何为它编写一个测试呢?在我看来,Pandas库的大部分内容已经过测试——因此确保那里的核心功能与我的设置一起工作似乎是一种浪费。但是,也许不是。或者,这可能是一个重构问题,将其分解为更小的部分

不管怎样,这是代码。。。如有任何见解,将不胜感激

def process_file(import_id=None):
    all_files = glob.glob(config.IMPORT_DIRECTORY + "*.txt")

    if len(all_files) == 0:
        return []

    import_data = (pd.read_csv(f, sep='~', encoding='latin-1',
                               warn_bad_lines=True, error_bad_lines=False,
                               low_memory=False) for f in all_files)

    data = pd.concat(import_data, ignore_index=True, sort=False)
    data.columns = [col.lower() for col in data.columns]
    data = data.where((pd.notnull(data)), None)

    data['import_id'] = import_id
    data['date'] = data['date'].apply(lambda x: convert_date_string(x))

    insert_data_into_database(data=data, table='sales')
    return all_files

总的来说,我不会去测试熊猫或任何其他依赖性。在我看来,重要的是要确保我使用的包得到了良好的开发和支持,然后对它进行测试将是多余的。熊猫是一个很好的支持包

至于您对特定函数的问题以及对测试的兴趣,我强烈建议您查看python包(您很幸运,目前只针对python)。它提供模拟数据并生成用于测试目的的边缘案例

他们文档中的一个示例:

from hypothesis import given
from hypothesis.strategies import text

@given(text())
def test_decode_inverts_encode(s):
    assert decode(encode(s)) == s
在这里,您告诉它函数需要接收文本作为输入,包将使用满足条件的不同变量多次运行它。它还将尝试各种边缘情况


一旦实现,它可以做更多的事情。

主要有两种测试——适当的单元测试和集成测试

顾名思义,单元测试是孤立地测试程序的“单元”(函数、类……)(不考虑它们如何与其他单元交互)。这当然要求这些单元可以单独测试。例如,一个纯函数(一个从其输入计算结果的函数,其中结果仅取决于输入,并且对于相同的输入总是相同的,并且没有任何副作用)非常容易测试,而一个从文件系统上硬编码路径读取数据的函数,向硬编码url发出http请求并更新数据库(其连接数据也是硬编码的)几乎不可能单独测试(实际上几乎不可能测试)

因此,第一点是在编写代码时要考虑到可测试性:支持具有单一明确职责和尽可能少的依赖项的小型、集中的单元(最好将它们的依赖项作为参数,这样您就可以传递模拟)。这当然有点像柏拉图式的理想,但这仍然是一个值得追求的目标。作为最后的手段,当您无法摆脱依赖项或参数化它们时,您可以使用类似于
mock
的包,它将用具有类似接口的伪对象替换依赖项

集成测试是从更高的级别测试整个子系统-例如,对于网站项目,您可能希望测试如果您提交“联系人”表单,电子邮件是否发送到给定的地址,以及数据是否也存储在数据库中。显然,您希望使用一次性测试数据库和一次性测试邮箱来实现这一点


您发布的函数可能做得太多了—它读取文件、构建panda数据帧、应用一些处理并将内容存储在数据库中。您可能希望尝试将其分解为多个函数—一个用于获取文件列表,一个用于从文件中收集数据,一个用于处理数据,等等,您已经有了一个将数据存储在数据库中的函数—并重写“process_files”(实际上不仅仅是处理)来调用这些函数。这将使单独测试每个部件变得更容易。完成此操作后,您可以使用
mock
测试“process_file”函数并检查它是否使用预期参数调用其他函数,或者在测试目录和测试数据库中运行它并检查数据库中的结果。

如何测试简单函数?检查各种输入是否提供正确的输出。你试着找到一些会破坏函数的东西,看看它是否会以你想要的方式反应。举个例子,如果你的导入目录中只有一个匹配文件。@ TraviSvx很高兴如果我能帮助你,然后自由地回答这个问题。