在我的 python 项目中,我有以下文件夹结构:
src
foo
foo.py
baa
baa.py
test
foo
baa
并且想要 生成一个单元测试文件
test/foo/test_foo.py
来测试 src/foo/foo.py
。
在 PyCharm 中我可以
Ctrl+Shift+T
和 这会生成一个新的测试文件并使用某种测试方法对其进行初始化。另请参阅 https://www.jetbrains.com/help/pycharm/creating-tests.html
但是:
PyCharm 不会自动检测所需的目标目录
test/foo
,但建议使用源目录 src/foo
。这将导致测试文件位于与测试文件相同的文件夹中。
该组合键不适用于不包含类或函数但仅提供属性的文件/模块。
PyCharm 生成不需要的 init.py 文件。
因此 PyCharm 的内置单元测试生成对我不起作用。
=> 为 Python 生成单元测试的推荐方法是什么?
我也尝试使用UnitTestBot或pynguin但无法生成相应的测试文件。手动创建所有文件夹和文件将是一项乏味的任务,我希望现代 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
这是一个脚本的初稿,该脚本为缺失的测试文件生成单元测试框架(我要求 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