pytest-hardpy¶
pytest-hardpy is a pytest plugin that helps you save test data in a database for test reporting and viewing test data in the web interface.
Plugin registration¶
To use the pytest-hardpy you need to enable it.
You can do this via the pytest.ini
file.
Example:
# pytest.ini
[pytest]
addopts = --hardpy-pt
Another way to enable a plugin without pytest.ini
file is to run tests with the option --hardpy-pt
.
pytest --hardpy-pt tests
If tests are run via hardpy panel, then the pytest-hardpy plugin will be enabled for tests by default.
Functions¶
set_dut_info¶
This function records a dictionary containing information about the test stand. When called again, the information will be added to DB.
Arguments:
info
(dict): DUT info
Example:
def test_dut_info():
set_dut_info({"batch": "test_batch", "board_rev": "rev_1"})
set_dut_serial_number¶
Writes a string with a serial number.
When called again, the exception DuplicateSerialNumberError
will be caused.
Arguments:
serial_number
(str): DUT serial number
Example:
def test_serial_number():
set_dut_serial_number("1234")
set_dut_part_number¶
Writes a string with a part number.
When called again, the exception DuplicatePartNumberError
will be caused.
Arguments:
part_number
(str): DUT part number
Example:
def test_part_number():
set_dut_part_number("part_1")
set_stand_name¶
Writes a string with a test stand name.
When called again, the exception DuplicateTestStandNameError
will be caused.
Arguments:
location
(str): test stand location
Example:
def test_stand_name():
set_stand_name("name 1")
set_stand_info¶
Writes a dictionary with information about the test stand. When called again, the information will be added to DB.
Arguments:
info
(dict): test stand info
Example:
def test_stand_info():
set_stand_info({"geo": "Belgrade"})
set_stand_location¶
Writes a string with a test stand location.
When called again, the exception DuplicateTestStandLocationError
will be caused.
Example:
def test_stand_info():
set_stand_location("Moon")
set_driver_info¶
The function records a dictionary containing information about the test stand driver. The data is updated with new information each time the function is called.
Driver data is stored in both statestore and runstore databases.
Arguments:
drivers
(dict): A dictionary of drivers, where keys are driver names and values are driver-specific data.
Example:
def test_driver_info():
drivers = {
"driver_1": {
"name": "Driver A",
"type": "network"
}
}
set_driver_info(drivers)
set_case_artifact¶
Writes a dictionary with a test case artifact. When called again, the data will be added to DB.
Artifacts are saved only in the runstore database because the state in statestore and case artifact must be separated.
Arguments:
data
(dict): data
Example:
def test_case_artifact():
set_case_artifact({"data_str": "123DATA"})
set_module_artifact¶
Writes a dictionary with a module test artifact. When called again, the data will be added to DB.
Artifacts are saved only in the runstore database because the state in statestore and module artifact must be separated.
Arguments:
data
(dict): data
Example:
def test_module_artifact():
set_module_artifact({"data_str": "456DATA"})
set_run_artifact¶
Writes a dictionary with a test run artifact. When called again, the data will be added to DB.
Artifacts are saved only in the runstore database because the state in statestore and run artifact must be separated.
Arguments:
data
(dict): data
Example:
def test_run_artifact():
set_run_artifact({"data_str": "789DATA"})
set_message¶
Writes a string with a message. If a message is sent without a key, the key will be generated automatically and the messages will be appended. If the message is sent with a known key, it will be updated.
Arguments:
msg
(str): Message content.msg_key
(Optional[str]): Message ID. If not specified, a random ID will be generated.
Example:
def test_message():
set_message("Test message")
set_message("Update message 1", "msg_upd")
set_message("Update message 2", "msg_upd")
set_operator_message¶
Sets an operator message in the statestore database and updates the database. Does not provide user interaction unlike the run_dialog_box function.
Arguments:
msg
(str): The message to be displayed.title
(str | None): The optional title for the message.block
(bool=True): If True, the function will block until the message is closed.image
(ImageComponent | None): Image information.font_size
: (int=14): Text font size.
Example:
from hardpy import set_operator_message
def test_set_operator_msg():
set_operator_message(msg="This is a sample operator message.", title="Important Notice")
clear_operator_message¶
Clears the current message to the operator if it exists, otherwise does nothing.
Example:
from time import sleep
from hardpy import set_operator_message, clear_operator_message
def test_clear_operator_msg():
hardpy.set_operator_message(msg="Clearing operator message.", title="Operator message", block=False)
sleep(2)
clear_operator_message()
run_dialog_box¶
Displays a dialog box and updates the dialog_box
field in the statestore database.
Arguments:
dialog_box_data
(DialogBox): Data for the dialog box.
Returns:
- (Any): An object containing the user's response.
The type of the return value depends on the widget type:
- Without widget (BASE): bool.
- NUMERIC_INPUT: float.
- TEXT_INPUT: str.
- RADIOBUTTON: str.
- CHECKBOX: List(str).
- MULTISTEP: bool.
Raises
ValueError
: If themessage
argument is empty.
Example:
from hardpy import dialog_box
def test_text_input():
dbx = DialogBox(
dialog_text="Type 'ok' and press the Confirm button",
title_bar="Example of text input",
widget=TextInputWidget(),
image=ImageComponent(address="assets/test.png", width=50),
)
response = run_dialog_box(dbx)
set_message(f"Entered text {response}")
assert response == "ok", "The entered text is not correct"
get_current_report¶
Returns the current report from the database runstore.
Returns:
- (ResultRunStore | None): report, or None if not found or invalid
Example:
def test_current_report():
report = get_current_report()
get_current_attempt¶
Returns the num of current attempt.
Returns:
- (int): num of current attempt
Example:
@pytest.mark.attempt(5)
def test_attempt_message():
attempt = hardpy.get_current_attempt()
hardpy.set_message(f"Current attempt {attempt}")
if attempt < 5:
assert False
Class¶
DialogBox¶
The class is used to configure the dialogue box and is used with the run_dialog_box function.
Arguments:
dialog_text
(str): The text of the dialog box.title_bar
(str | None): The title bar of the dialog box. If the title_bar field is missing, it is the case name.widget
(IWidget | None): Widget information.image
(ImageComponent | None): Image information.font_size
: (int=14): Text font size.
Widget list:
- Base, only dialog text;
- Text input, TextInputWidget;
- Numeric input, NumericInputWidget;
- Radiobutton, RadiobuttonWidget;
- Checkbox, CheckboxWidget;
- Multistep, MultistepWidget.
Example:
DialogBox(title_bar="Example title", dialog_text="Example text")
TextInputWidget¶
The class is used to configure text input widget in dialog box. Further information can be found in section text input field. Widget returns a string when using run_dialog_box.
Example:
dbx = DialogBox(
dialog_text="Type 'ok' and press the Confirm button",
title_bar="Example of text input",
widget=TextInputWidget(),
)
response = run_dialog_box(dbx)
NumericInputWidget¶
The class is used to configure numeric input widget in dialog box. Further information can be found in section numeric input field. Widget returns a float when using run_dialog_box.
Example:
dbx = DialogBox(
dialog_text=f"Enter the number {test_num} and press the Confirm button",
title_bar="Example of entering a number",
widget=NumericInputWidget(),
)
response = int(run_dialog_box(dbx))
RadiobuttonWidget¶
The class is used to configure radiobutton widget in dialog box. Further information can be found in section radiobutton. Widget returns a string with the selected radiobutton value run_dialog_box.
Arguments:
fields
(list[str]): Radiobutton fields.
Example:
dbx = DialogBox(
dialog_text='Select item "one" out of several and click Confirm.',
title_bar="Radiobutton example",
widget=RadiobuttonWidget(fields=["one", "two", "three"]),
)
response = run_dialog_box(dbx)
CheckboxWidget¶
The class is used to configure checkbox widget in dialog box. Further information can be found in section checkbox. Widget returns a list of string with the selected checkbox value run_dialog_box.
Arguments:
fields
(list[str]): Checkbox fields.
Example:
dbx = DialogBox(
dialog_text='Select items "one" and "two" and click the Confirm button',
title_bar="Checkbox example",
widget=CheckboxWidget(fields=["one", "two", "three"]),
)
response = run_dialog_box(dbx)
StepWidget¶
The class is used to configure the step for the multistep widget in dialog box.
Arguments:
title
(str): Step title.text
(str | None): Step text.image
(ImageComponent | None): Step image.
Example:
StepWidget("Step 1", text="Content for step")
MultistepWidget¶
The class is used to configure multistep widget in dialog box. Further information can be found in section multiple steps.
Arguments:
steps
(list[StepWidget]): A list with info about the steps.
Example:
steps = [
StepWidget("Step 1", text="Content for step"),
StepWidget("Step 2", text="Content for step 2", image=ImageComponent(address="assets/image.png", width=100)),
]
dbx = DialogBox(dialog_text="Follow the steps and click Confirm", widget=MultistepWidget(steps))
response = run_dialog_box(dbx)
ImageComponent¶
A class for configuring an image for a dialogue box or operator message box and is used with the run_dialog_box and set_operator_message functions.
Arguments:
address
(str): Image address.width
(int | None): Image width in %.border
(int | None): Image border width.
Example:
ImageComponent(address="assets/image.png", width=100)
CouchdbLoader¶
Used to write reports to the database CouchDB.
Report names (revision id) are automatically generated based on the test
completion date and the device serial number.
If the serial number dut is empty, a random identifier with
prefix no_serial
is used.
The random identifier is a unique string generated using the uuid4()
function from the uuid
module in Python.
This allows for easy identification and sorting of reports.
- Valid report name:
report_1726496218_1234567890
- Valid report name (no serial number):
report_1726496218_no_serial_808007
Example:
# conftest
def finish_executing():
report = get_current_report()
if report:
loader = CouchdbLoader(CouchdbConfig())
loader.load(report)
@pytest.fixture(scope="session", autouse=True)
def fill_actions_after_test(post_run_functions: list):
post_run_functions.append(finish_executing)
yield
Fixture¶
post_run_functions¶
To execute actions at the end of testing, you can use the fixture post_run_functions.
This fixture is a list[Callable]
and you can write functions into it that must be executed at the end of testing.
Fill this list in conftest.py and functions from this list will be called after tests run (at the end of pytest_sessionfinish).
Returns:
- (list[Callable]): list of post run methods
Example:
# conftest.py file
def finish_executing():
print("Pytest finished")
@pytest.fixture(scope="session", autouse=True)
def fill_actions_after_test(post_run_functions: list):
post_run_functions.append(finish_executing)
yield
Marker¶
case_name¶
Sets a text name for the test case (default: function name)
Example:
@pytest.mark.case_name("Simple case 1")
def test_one():
assert True
module_name¶
Sets a text name for the test module (file) (default: module name)
Example:
pytestmark = pytest.mark.module_name("Module 1")
dependency¶
Skips the test case/module if the main test fails/skipped/errored. For more information, see the example skip test
Example:
#test_1.py
def test_one():
assert False
@pytest.mark.dependency("test_1::test_one")
def test_two():
assert True
attempt¶
If a test is marked attempt
, it will be repeated if it fails the number of
attempts specified by the mark.
The test will be repeated until it is passed.
There is a 1 second pause between attempts.
For more information, see the example attempts.
Example:
@pytest.mark.attempt(5)
def test_attempts():
assert False
Options¶
pytest-hardpy has several options to run:
hardpy-pt¶
Option to enable the pytest-hardpy plugin.
--hardpy-pt
hardpy-db-url¶
The CouchDB instance url for the statestore and runstore databases.
The default is http://dev:dev@localhost:5984/
.
--hardpy-db-url
hardpy-sp¶
Internal socket port for passing backend data (such as a dialog box) to running pytest tests. The default is 6525.
--hardpy-sp
hardpy-sh¶
Internal socket host for passing backend data (such as a dialog box) to running pytest tests. The default is localhost.
--hardpy-sh
hardpy-clear-database¶
Option to clean statestore and runstore databases before running pytest.
--hardpy-clear-database