如何使用 src 和测试文件夹结构生成 Python 单元测试(最好与 PyCharm 集成)?

问题描述 投票:0回答:1

在我的 python 项目中,我有以下文件夹结构:

src
  foo
    foo.py
  baa
    baa.py
test
  foo
  baa

并且想要 生成一个单元测试文件

test/foo/test_foo.py
来测试
src/foo/foo.py

在 PyCharm 中我可以

  • 转到 foo.py 中的某个地方并且
  • 使用组合键
    Ctrl+Shift+T
  • 选择“创建新测试”选项。

这会生成一个新的测试文件并使用某种测试方法对其进行初始化。另请参阅 https://www.jetbrains.com/help/pycharm/creating-tests.html

但是:

  • PyCharm 不会自动检测所需的目标目录

    test/foo
    ,但建议使用源目录
    src/foo
    。这将导致测试文件位于与测试文件相同的文件夹中。

  • 该组合键不适用于不包含类或函数但仅提供属性的文件/模块。

  • PyCharm 生成不需要的 init.py 文件。

因此 PyCharm 的内置单元测试生成对我不起作用。

=> 为 Python 生成单元测试的推荐方法是什么?

我也尝试使用UnitTestBotpynguin但无法生成相应的测试文件。手动创建所有文件夹和文件将是一项乏味的任务,我希望现代 IDE 能为我完成(部分)工作。

您可能会认为应该先编写测试。因此,如果有一个选项可以以相反的方式进行操作并从退出测试中生成测试文件......这也会很有帮助。

另一种选择可能是使用 GitHub Copilot。但是,我不允许在工作中使用它,并且不想将我们的代码发送到远程服务器。因此,我仍在寻找更保守的方法。

另一种方法是使用自定义脚本,循环遍历现有的 src 文件夹结构,并至少在测试文件夹中创建相应的文件夹和文件。我希望有一个现有的解决方案,但尚未找到。

相关:

https://github.com/UnitTestBot/UTBotJava/issues/2670

https://github.com/se2p/pynguin/issues/52

pytest或python中的任何测试工具可以自动生成单元测试吗?

https://www.strictmode.io/articles/using-github-copilot-for-testing

https://dev.to/this-is-learning/copilot-chat-writes-unit-tests-for-you-1c82

python unit-testing pycharm pytest code-generation
1个回答
0
投票

这是一个脚本的初稿,该脚本为缺失的测试文件生成单元测试框架(我要求 AI 为我生成该脚本并对其进行了一些重构):

import inspect
import os


def main():
    src_dir = 'src'
    test_dir = 'test'
    generate_unit_test_skeleton(src_dir, test_dir)


def generate_unit_test_skeleton(src_dir, test_dir):
    # Loop over all folders and files in src directory
    for root, dirs, files in os.walk(src_dir):
        # Get the relative path of the current folder or file
        relative_folder_path = os.path.relpath(root, src_dir)

        # Create the corresponding unit test folder in test directory
        test_folder = generate_test_folder_if_not_exists(relative_folder_path, test_dir)

        # Loop over all files in the current folder
        for file in files:
            # Check if the file has a .py extension
            if file.endswith('.py'):
                generate_unit_tests_for_file(file, relative_folder_path, root, test_folder)


def generate_unit_tests_for_file(file, relative_directory, root, test_directory):
    # Create the corresponding unit test file in test directory
    generated_test_file_path = generate_unit_test_file(file, relative_directory, test_directory)

    if generated_test_file_path is not None:
        # Get all classes and functions defined in the original file
        classes, functions = determine_members(file, relative_directory)

        # Generate test functions for each class and function
        generate_test_functions(generated_test_file_path, classes, functions)


def generate_test_functions(test_file_path, classes, functions):
    members = classes + functions
    with open(test_file_path, 'a') as f:
        for name, member in members:
            # Generate the test function name
            test_function_name = f'test_{name}'

            # Write the test function to the test file
            f.write(f'def {test_function_name}():\n')
            f.write(f'    # TODO: Implement test\n')
            f.write(f'    pass\n')
            f.write('\n')


def determine_members(file, relative_directory):
    # Get the module name
    module_name = os.path.splitext(file)[0]

    # Import the module
    directory_import_path = relative_directory.replace('\\', '.')
    import_path = f'{directory_import_path}.{module_name}'
    module = __import__(import_path, fromlist=[module_name])

    # Get all classes and functions defined in the module
    classes = inspect.getmembers(module, inspect.isclass)
    functions = inspect.getmembers(module, inspect.isfunction)
    return classes, functions


def generate_unit_test_file(file, relative_directory, test_directory):
    test_file_path = os.path.join(test_directory, f'test_{file}')
    if os.path.exists(test_file_path):
        return None
    else:
        # Open the test file in write mode
        with open(test_file_path, 'w') as f:
            # Write the initial import statement
            import_path = determine_import_path(file, relative_directory)
            f.write(f'import {import_path}\n')
            f.write('\n')
    return test_file_path


def determine_import_path(file, relative_directory):
    directory_import_path = relative_directory.replace('////', '.')
    module_name = os.path.splitext(file)[0]
    import_path = f'{directory_import_path}.{module_name}'
    return import_path


def generate_test_folder_if_not_exists(relative_path, test_dir):
    test_folder = os.path.join(test_dir, relative_path)
    if not os.path.exists(test_folder):
        os.makedirs(test_folder)
    return test_folder


if __name__ == '__main__':
    main()

结果示例:

import foo.foo

def test_Language():
    # TODO: Implement test
    pass

def test_Layout():
    # TODO: Implement test
    pass

def test_SimpleNamespace():
    # TODO: Implement test
    pass

def test_data_frame_preview():
    # TODO: Implement test
    pass
© www.soinside.com 2019 - 2024. All rights reserved.