I am writing an integration test for saving data disk as JSON. The desired behavior: when a record already exists in the JSON file, the application should raise an exception before overwriting existing JSON records. Since the exception being raised is desired behavior, the integration test should catch this exception, and pass.
Although the error is raised within the Qt Event Loop, it appears that pytest does not catch it. What can I do to ensure that pytest catches the ValueError I raise?
Pytest did not catch the exception...
records = json.loads(database.read_text())["records"].keys()
if record_name in records:
> with pytest.raises(ValueError) as execinfo:
E Failed: DID NOT RAISE <class 'ValueError'>
... but it does appear in stderr -- caught by the qt event loop.
---------------------------- Captured stderr call -----------------------------
Exceptions caught in Qt event loop:
________________________________________________________________________________
Traceback (most recent call last):
File "<string>", line 3, in save
File "C:\Users\me\AppData\Local\Programs\Python\Python312\Lib\unittest\mock.py", line 1137, in __call__
return self._mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\me\AppData\Local\Programs\Python\Python312\Lib\unittest\mock.py", line 1141, in _mock_call
return self._execute_mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\me\AppData\Local\Programs\Python\Python312\Lib\unittest\mock.py", line 1202, in _execute_mock_call
result = effect(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\me\Desktop\ProjectDir\.venv\Lib\site-packages\pytest_mock\plugin.py", line 177, in wrapper
r = method(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^
File "c:\Users\me\Desktop\ProjectDir\record\home.py", line 132, in save
record.toJSON()
File "c:\Users\me\Desktop\ProjectDir\record\record.py", line 53, in toJSON
raise ValueError(f"record '{self.name}' already exists in database")
ValueError: record 'existing_record_name' already exists in database
The Integration Test: this snippet is part of a larger integration test. I expect this mouse click signal to trigger a slot method that raises a ValueError.
## code within the test_save_new_record() integration test
records = json.loads(database.read_text())["records"].keys()
if record_name in records:
with pytest.raises(ValueError) as execinfo:
qtbot.mouseClick(home_widget.save_button, Qt.MouseButton.LeftButton) # << exception SHOULD be raised
else:
# rest of test...
The slot to which the signal is connected:
class HomeWidget(QWidget, Ui_widget):
def __init__(self):
self.save_button.clicked.connect(self.save) # <<<
The body of the slot method:
def save(self):
selected_record = self.record_list.currentItem()
if selected_record:
self.record_builder.name_edited = selected_record.text()
record = self.record_builder.build()
record.toJSON() # <<< this raises the exception
Finally, we come to the method where data is saved to disk, and the exception is raised:
def toJSON(self):
# if the record already exists, raise ValueError exception
db = json.loads(self.record_db.read_text(encoding="UTF-8"))
if self.name in db["records"].keys():
raise ValueError(f"record '{self.name}' already exists in database") # <<<
else:
# rest of method ...
Instead of using pytest.raises(ValueError)
, use pytestqt
to intercept exceptions in Qt's event loop when testing:
with qtbot.capture_exceptions() as exceptions:
qtbot.mouseClick(
button, QtCore.MouseButton.LeftButton
)
See the documentation here.