我正在尝试使用Behave on Python。 我想知道是否有办法将我的 .py 文件放在其他地方,而不是被迫将它们全部放在“steps”文件夹中。我当前的结构看起来像这样
tests/
features/
steps/ #all code inside here, for now
我想要完成的是类似的事情
tests/
features/ #with all the .feature files
login/ #with all the .py files for logging in inside a service
models/ #with all the .py files that represents a given object
and so on
我在 Behave 之前使用的唯一 BDD 框架是 Cucumber with Java,它允许在我想要的任何地方插入步骤定义(其余的由 Cucumber 本身处理)。 我问这个问题是因为我希望在我的项目中包含很多类,以便以更好的方式组织我的代码。
这可能有点晚了,但您可以执行以下操作:
具有这样的结构:
tests/
features/
steps/
login
main_menu
all_steps.py
在步骤的子文件夹中,您可以使用实现创建 _steps.py 文件,然后在 all_steps.py(或您想要的命名方式)中,您只需导入它们:
from tests.steps.login.<feature>_step import *
from tests.steps.main_menu.<feature>_step import *
etc
当您运行它时,它应该找到步骤文件。或者,您可以将文件放在项目中的任何位置,只要您有 1 个 Steps 文件夹,并且文件中包含一个文件(如果您导入所有步骤)
首先,来自Behavior文档(Release 1.2.7.dev0):
behave 适用于三种类型的文件:
- 由您的业务分析师/发起人/其中包含您的行为场景的人员编写的功能文件,以及
- “steps”目录,其中包含场景的 Python 步骤实现。
- 可选的一些环境控制(在步骤、场景、功能或整个过程之前和之后运行的代码) 射击比赛)。
因此需要一个
steps/
目录。
为了实现与您想要的类似的解决方法,我尝试在
/steps
目录中创建一个子目录:/steps/deeper/
并在那里插入我的 Python 文件:/steps/deeper/testing.py
。运行behave后,我收到“NotImplementedError”,这意味着找不到/deeper/testing.py
中的步骤定义。
行为似乎不会通过
steps/
目录的子目录递归搜索任何其他 Python 文件。
至于你想要做什么,我认为这是一个不错的组织想法,但由于它不可行,你可以这样做:而不是在你的 tests/
目录中为 Python 文件使用
directories,为什么不拥有一个您的 Python 文件有良好的命名约定并将相关函数分离到它们自己的 Python 文件中吗?那就是:
tests/
features/
steps/
login_prompt.py # contains all the functions for logging in inside a service
login_ssh.py # contains all the functions for SSH login
models_default.py # contains all the functions for the default object
models_custom.py # contains all the functions for a custom object
and so on...
当然,此时,是否将它们分成不同的 Python 文件并不重要,因为调用时,behave 会搜索
steps/
中的所有 Python 文件,但为了组织的目的,它实现了相同的效果。
您可以使用其他方法来完成此操作,如下所示:
def import_steps_from_subdirs(dir_path):
for directory in walk(dir_path):
current_directory = directory[0] + '/'
all_modules = [module_info[1] for module_info in iter_modules(path=[current_directory])]
current_directory = current_directory.replace(Resources.BASE_DIR + '/', '')
for module in all_modules:
import_module(current_directory.replace('/', '.') + module)
然后在before_all层调用该方法
我采用了
Bidonus'
解决方案如下,这应该是可复制/粘贴的:
features/helpers/import_helper.py
from importlib import import_module
from pkgutil import iter_modules
from os import walk
from pathlib import Path
def import_steps(steps_dir: Path):
assert steps_dir.exists()
for (directory, _, _) in walk(steps_dir):
if "__pycache__" in directory:
continue
if directory == str(steps_dir):
continue
current_directory = directory + "/"
print(f"Importing Additional Steps: {current_directory}")
all_modules = [module_info[1]
for module_info in iter_modules(path=[str(current_directory)])]
current_directory = current_directory.replace(str(steps_dir) + '/', '')
for module in all_modules:
module_path = current_directory.replace('/', '.') + module
import_module(module_path)
然后创建了一个
__init__.py
文件:
features/steps/__init__.py
from features.helpers.import_steps import import_steps
from pathlib import Path
STEPS_DIR_NAME = "steps"
STEPS_DIR = Path(__file__).parent
assert STEPS_DIR.name == STEPS_DIR_NAME
import_steps(STEPS_DIR)
现在,当我运行测试时,我首先得到:
Importing Additional Steps: /workspaces/XXXXX/features/steps/create_model/
Importing Additional Steps: /workspaces/XXXXX/features/steps/list_operations/
Importing Additional Steps: /workspaces/XXXXX/features/steps/assertions/
Importing Additional Steps: /workspaces/XXXXX/features/steps/assertions/list_assertions/
Importing Additional Steps: /workspaces/XXXXX/features/steps/context_managers/
Importing Additional Steps: /workspaces/XXXXX/features/steps/type_switchers/
Importing Additional Steps: /workspaces/XXXXX/features/steps/test_setup/
Importing Additional Steps: /workspaces/XXXXX/features/steps/dao_function_callers/
…
Test Results
…