【问题标题】:How to get user input in BDD framworks i.e. Behave如何在 BDD 框架中获取用户输入,即 Behave
【发布时间】:2021-11-03 16:22:32
【问题描述】:

我正在尝试一些 BDD 框架,例如 Python Behave。我想编写一些测试用例,用户应该能够在其中提供输入,进一步的测试执行将取决于用户输入。来自用户的输入应该可以在所有场景之间共享。 到目前为止,我找不到一个好的方法来做到这一点。我只尝试过 Behave,请提及是否有任何其他框架可以更好地服务于此目的:)

【问题讨论】:

  • 这样做的目的是什么?你能解释一下这个场景吗?
  • @JeromeJosephraj 的目的是我想利用 BDD 框架来自动测试 CPE 设备,用户应该能够在那里提供输入,例如,要求用户确认设备的灯闪烁,如果他们确认是,那么我们测试下一个场景,否则,我们跳过或只运行上述响应不相关的场景。我希望它有一些意义:)
  • 这不类似于客户通过网站提供输入吗?换句话说,您打算验证什么行为?那么如果客户输入“是”然后您测试特定场景,如果客户输入“否”那么您测试不同的场景会是这样吗?您如何在您的应用程序中获取客户输入的值?会通过 API 调用吗?很抱歉所有这些问题,因为我不熟悉 CPE 设备
  • @JeromeJosephraj 你是对的,将要测试的场景将取决于来自用户的内容。我计划有一个客户可以看到的 GUI,并且可以通过 API 调用进行交互。

标签: python automation frameworks bdd qa


【解决方案1】:

除了 BDD,您将如何自动化预期用户交互的测试?

  • 您可以对输入(或其效果)进行动画处理,
  • 要么您必须等待人工干预并完成任务。

我已经为 Python 中的 GUI 应用程序实现了 BDD 测试套件。 BDD 部分与通常的自动化测试套件没有什么不同。
如果您想测试一个简单的终端应用程序,您可以在包含输入的文件中使用管道。

但是,如果您打算测试一个富终端或图形应用程序,您应该为这个特定框架寻找一个测试驱动程序。例如,对于 Qt,QTest 提供 mouseClickkeyPressqWaitForWindowActive、...

BDD 仅更改测试的结构,而不是:

def test__add_one_int_with_another__gets_the_result():
    driver = initiate_test_driver()
    driver.click_number(1)
    driver.click_operator("+")
    driver.click_number(1)
    driver.click_enter()
    assert driver.get_displayed_result() == "2"

你将拥有

Feature: simple calculator

Scenario: adding two positive integers
   Given: the application is started
    When: I press <x>
     And: I press +
     And: I press <y>
     And: I press Enter
    Then: I get the result <result>

Examples: Computations
   | x | y | result |
   | 1 | 1 | 2      |
def the_application_is_started(ctxt):
    ctxt.driver = initiate_test_driver()

def i_press(ctxt, something):
    if something in (0,1,2,3,4,5,6,7,8,9):
        ctxt.driver.click_number(something)
    elif something in ("+", "-", "*", "/"):
        ctxt.driver.click_operator(something)

def i_get_the_result(ctxt, result):
    assert ctxt.driver.get_displayed_result() == result

编辑:正如这个答案的 cmets 中所问的,这是一个自动化测试期间用户交互的示例。

from PyQt5 import QtWidgets, QtCore, Qt, QtGui


class DiodeSwitchWindow(QtWidgets.QMainWindow):
    def __init__(self) -> None:
        super().__init__(None)  # a top window has no parent
        self.setWindowTitle("Diode Switcher")
        self.diode_switch_panel = QtWidgets.QWidget(self)
        self.diode_switch_panel.switch_diode_button = QtWidgets.QPushButton(self.diode_switch_panel)
        self.diode_switch_panel.switch_diode_button.setText("Switch")
        self.diode_switch_panel.diode_label = QtWidgets.QLabel(self.diode_switch_panel)
        self.diode_switch_panel.diode_icon_on = QtGui.QPixmap(16, 16)
        self.diode_switch_panel.diode_icon_on.fill(QtGui.QColor("#0F0"))  # green
        self.diode_switch_panel.diode_icon_off = QtGui.QPixmap(16, 16)
        self.diode_switch_panel.diode_icon_off.fill(QtGui.QColor("#F00"))  # red
        self.diode_switch_panel.diode_label.setPixmap(self.diode_switch_panel.diode_icon_off)
        self.diode_switch_panel.switch_state = False  # off
        self.diode_switch_panel.switch_diode_button.clicked.connect(lambda _: self.switch())
        layout = QtWidgets.QHBoxLayout()
        layout.addWidget(self.diode_switch_panel.switch_diode_button)
        layout.addWidget(self.diode_switch_panel.diode_label)
        self.diode_switch_panel.setLayout(layout)
        self.setCentralWidget(self.diode_switch_panel)

    def switch(self) -> None:
        if self.diode_switch_panel.switch_state is True:
            self.diode_switch_panel.diode_label.setPixmap(self.diode_switch_panel.diode_icon_off)
        else:
            self.diode_switch_panel.diode_label.setPixmap(self.diode_switch_panel.diode_icon_on)
        self.diode_switch_panel.switch_state = not self.diode_switch_panel.switch_state


def test_switch() -> None:
    from PyQt5.QtTest import QTest

    # setup the application under test
    app = QtWidgets.QApplication([])
    main_window = DiodeSwitchWindow()
    main_window.setVisible(True)
    # no exec !!!
    assert main_window.diode_switch_panel.switch_state is False

    # simulate user interaction
    QTest.mouseClick(main_window.diode_switch_panel.switch_diode_button, QtCore.Qt.LeftButton)
    # ask the user to check visually that the diode has switched from off to on
    answer = QtWidgets.QMessageBox(
        QtWidgets.QMessageBox.Question,  # icon
        "Manual validation required",  # title
        "Is the diode green (ON) ?",  # question
        QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes,  # buttons
        None,  # no parent
    ).exec()
    assert answer == QtWidgets.QMessageBox.Yes

    # simulate user interaction again
    QTest.mouseClick(main_window.diode_switch_panel.switch_diode_button, QtCore.Qt.LeftButton)
    # ask the user to check visually that the diode has switched from on to off
    answer = QtWidgets.QMessageBox(
        QtWidgets.QMessageBox.Question,  # icon
        "Manual validation required",  # title
        "Is the diode red (OFF) ?",  # question
        QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes,  # buttons
        None,  # no parent
    ).exec()
    assert answer == QtWidgets.QMessageBox.Yes


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    main_window = DiodeSwitchWindow()
    main_window.setVisible(True)
    app.exec()

如果您运行此文件 ($ python so69103776.py),它将显示应用程序并让您使用它,直到您退出它(关闭窗口)。 如果您在此文件上运行测试运行程序(例如$ pytest so69103776.py),它将打开一个弹出窗口供您回答,两次。

【讨论】:

  • 这是我更感兴趣的想法,即具有人工干预可能性的 BDD。到目前为止,我还没有找到一个好的方法来做到这一点。例如,测试运行并要求用户(在这种情况下为测试人员)检查路由器上的灯是否闪烁,用户在 GUI 上看到这一点,当用户确认状态时,下一个测试将运行。
  • @a21a 但是BDD与问题无关,这就是我想解释的。 BDD 是关于代码结构的,但在测试驱动方面与常规测试自动化相比并没有太大变化。如果您希望在测试期间进行用户交互,请让测试方法调用一个函数以显示一个窗口(使用您选择的框架:Tk、Qt、...)。您希望我在回答中添加此类测试的示例吗?
  • 好的,举个例子肯定会有所帮助
  • @a21a 示例已添加,如果适合您,请考虑接受答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-17
  • 1970-01-01
相关资源
最近更新 更多