Testing Guide¶
Comprehensive documentation for the ezqt_app test suite. The test suite ensures reliability and correctness of all services, widgets, and workflows through three complementary scopes.
Test Structure¶
Directory Organization¶
tests/
├── conftest.py # Shared fixtures and pytest configuration
├── pytest.ini # Markers, coverage, and runner settings
├── run_tests.py # Test runner script with CLI options
├── unit/ # Fast, isolated unit tests
│ ├── test_services/
│ │ ├── test_app_functions.py # ConfigService tests
│ │ ├── test_app_settings.py # SettingsService tests
│ │ └── test_translation_manager.py # TranslationManager tests
│ ├── test_utils/
│ │ └── test_cli.py # CLI initialization tests
│ └── test_widgets/
│ ├── test_core/
│ │ ├── test_ez_app.py # EzApplication tests
│ │ ├── test_header.py # Header widget tests
│ │ ├── test_menu.py # Menu widget tests
│ │ ├── test_page_container.py # PageContainer tests
│ │ └── test_settings_panel.py # SettingsPanel tests
│ └── test_extended/
│ └── test_setting_widgets.py # Extended setting widget tests
└── integration/ # Cross-component integration tests
├── test_application/
│ └── test_app_flow.py # Full app initialization flow
└── test_services/
└── test_translations.py # Translation workflow integration
Test Statistics¶
| Category | Files | Tests |
|---|---|---|
| Services | 3 | ~27 |
| Utils | 1 | 3 |
| Core Widgets | 5 | ~86 |
| Extended Widgets | 1 | ~33 |
| Integration | 2 | ~27 |
| Total | 12 | ~176 |
Unit Tests – Services¶
ConfigService¶
File: unit/test_services/test_app_functions.py – 5 tests (TestConfigServiceV5)
- Load config successfully from project root
- Load config when file is missing (returns empty dict)
- Get config value by key
- Save config writes
app.config.yaml - Get package resource returns existing path
SettingsService¶
File: unit/test_services/test_app_settings.py – 8 tests (TestSettings)
- App settings shape and default values
- GUI settings shape and default values
- Settings mutability (read/write)
QSizeconsistency across settings- Boolean settings behavior
- Integer settings behavior
- String settings behavior
- Settings internal structure validation
TranslationManager¶
File: unit/test_services/test_translation_manager.py – 14 tests (TestTranslationManager)
- Init with default language (
en) - Language mapping (code → locale name)
- Available languages list
- Get current language code
- Get current language name
- Translate text when no translation is available (passthrough)
- Load language success (with mock translator)
- Load language failure (with mock file system and QCore)
- Register a widget for translation
- Unregister a widget from translation
- Clear all registered widgets
- Set translatable text on widget
- Load language by code helper
languageChangedsignal emission
Unit Tests – Utils¶
CLI¶
File: unit/test_utils/test_cli.py – 3 tests
- CLI group initialization (Click app object exists)
- CLI lists expected commands (
init,test,docs,info, etc.) - CLI
--helpruns successfully and exits cleanly
Unit Tests – Core Widgets¶
EzApplication¶
File: unit/test_widgets/test_core/test_ez_app.py – 14 tests
- Inheritance from
QApplication - Class definition and module location
- Locale configuration success path
- Locale configuration failure path
- Environment variables setup (
QT_*flags) - High DPI scaling configuration
- Application properties (name, version)
- Environment setup with mocked
os.environ - Singleton behavior (single instance pattern)
- Method inheritance from QApplication
themeChangedsignal definition- Constructor signature validation
- Class-level documentation presence
themeChangedsignal on live instance
Header¶
File: unit/test_widgets/test_core/test_header.py – 20 tests
- Default parameter initialization
- Custom parameter initialization
- Layout structure (QHBoxLayout)
- Meta info frame presence and properties
- App logo label
- App name label
- App description label
- Buttons frame presence
- Buttons layout (QHBoxLayout)
- Settings button presence and type
- Minimize button presence and type
- Maximize button presence and type
- Close button presence and type
- Button list management
- Size policy (Expanding horizontal)
- Custom app name applied
- Custom description applied
- Button click signals (minimize, maximize, close, settings)
- Header fixed height constraint
- Header width size policy
Menu¶
File: unit/test_widgets/test_core/test_menu.py – 20 tests
- Default parameter initialization
- Custom widths (
expanded_width,collapsed_width) - Layout structure (QVBoxLayout)
- Main menu frame properties
- Main menu layout properties
- Toggle container presence
- Toggle layout properties
- Toggle button type and properties
- Menu dictionary initialization
- Button list management
- Icon list management
- Size constraints (min/max width)
- Toggle button visual properties
- Toggle button signal (
toggled) - Menu expansion capability
- Menu initial state (collapsed)
- Menu behavior with different widths
- Menu frame QSS properties
- Menu layout margins and spacing
- Menu object names
PageContainer¶
File: unit/test_widgets/test_core/test_page_container.py – 20 tests
- Default parameter initialization
- Initialization with parent widget
- Layout structure (QVBoxLayout)
- QStackedWidget presence
- Pages dictionary initialization
- Add page (name, type, registration)
- Add multiple pages
- Page object names
- Initial state (no pages)
- With pre-existing pages
- Add page with special characters in name
- Add page with empty name
- Add page with numeric name
- Layout margins validation
- Layout spacing validation
- QStackedWidget style sheet presence
- Frame properties (shape, shadow)
- Frame object name
- QFrame inheritance
- Size policy (Expanding)
SettingsPanel¶
File: unit/test_widgets/test_core/test_settings_panel.py – 12 tests
- Default parameter initialization
- Custom width parameter
- Initialization with parent widget
- Scroll area presence and properties
- Content container presence
- Theme container and label
- Theme layout properties
- Signals existence (
settingChanged) - Internal collections (settings dict, widgets list)
- Size constraints and size policy
set_widthmethod behavior- Panel without YAML config loading (graceful fallback)
Unit Tests – Extended Widgets¶
Setting Widgets¶
File: unit/test_widgets/test_extended/test_setting_widgets.py – ~33 tests
TestBaseSettingWidget – 2 tests¶
- Widget init and base properties
set_keymethod updates key
TestSettingToggle – 6 tests¶
- Default initialization
- Initialization with description
- UI components (label, toggle control)
- Toggle value behavior
set_valuemethodvalueChangedsignal
TestSettingSelect – 6 tests¶
- Default initialization
- Initialization with default value
- UI components (label, combo box)
valuepropertyget_value/set_valuemethodsvalueChangedsignal
TestSettingSlider – 7 tests¶
- Default initialization
- Initialization with custom range
- UI components (label, slider)
- Slider min/max properties
valuepropertyget_value/set_valuemethodsvalueChangedsignal
TestSettingText – 6 tests¶
- Default initialization
- Initialization with default value
- UI components (label, line edit)
valuepropertyget_value/set_valuemethodsvalueChangedsignal
TestSettingCheckbox – 6 tests¶
- Default initialization
- Initialization with default value
- UI components (label, checkbox)
valuepropertyget_value/set_valuemethodsvalueChangedsignal
Integration Tests¶
App Flow¶
File: integration/test_application/test_app_flow.py – 12 tests
- Application initialization (
EzApplication+EzQt_App) - App initialization with theme preset settings
- Window properties (title, geometry, flags)
- Menu functionality (addMenu, page switching)
- Header functionality (signals, buttons)
- Pages container behavior
- Settings panel presence and behavior
- Signal connections (header → app, menu → app)
- Theme loading from file path
- Window size on launch
- App cleanup (widget destruction)
- App without theme file (graceful fallback)
Translation Workflows¶
File: integration/test_services/test_translations.py – 15 tests
- TranslationManager integration (init → load → query)
- Translation helpers integration (module-level functions)
- Language change workflow (code → locale → UI update)
- Widget registration workflow (register → change language → retranslate)
- Translation text workflow (set text → change language → verify)
- Multiple successive language changes
- Widget retranslation workflow (register → change → verify text)
languageChangedsignal workflow (signal → slot triggered)- Translation file loading workflow (
.tsfile → load → verify) - Translation mapping workflow (source → target language)
- Translation error handling (invalid language code)
- TranslationManager singleton behavior
- Translation text setting workflow
- TranslationManager cleanup
- TranslationManager state persistence across language changes
Run Tests¶
python tests/run_tests.py --type unit
python tests/run_tests.py --type integration
python tests/run_tests.py --type robustness
python tests/run_tests.py --type all
With coverage:
Fast mode (excludes @pytest.mark.slow):
Parallel mode (requires pytest-xdist):
Using pytest directly:
Coverage¶
Generate report:
Terminal coverage details:
Open the HTML report:
Test Configuration¶
conftest.py – Shared Fixtures¶
Location: tests/conftest.py
| Fixture | Scope | Description |
|---|---|---|
qt_application |
session | EzApplication instance shared across all Qt tests |
qt_widget_cleanup |
function | Calls processEvents() for cleanup after each test |
ez_application_cleanup |
function | Fresh EzApplication instance for singleton tests |
wait_for_signal |
function | Helper to wait for a Qt signal with timeout |
mock_icon_path |
function | Temp PNG file for icon-loading tests |
mock_svg_path |
function | Temp SVG file for icon-loading tests |
mock_translation_files |
function | Temp .ts files (EN + FR) for translation tests |
mock_yaml_config |
function | Temp app.config.yaml for config service tests |
Test Markers¶
Configured in tests/pytest.ini:
| Marker | Description |
|---|---|
unit |
Unit tests (isolated modules/components) |
integration |
Cross-component behavior tests |
robustness |
Edge case and failure path tests |
slow |
Slow tests (exclude with -m "not slow") |
qt |
Tests requiring a live QApplication |
Usage:
# Run only unit tests
pytest -m unit
# Exclude slow tests
pytest -m "not slow"
# Run integration and robustness
pytest -m "integration or robustness"
# Run Qt-dependent tests only
pytest -m qt tests/unit/
Best Practices¶
- Keep tests deterministic and isolated.
- Use fixtures from
tests/conftest.pyinstead of ad-hoc setup. - Prefer integration tests on real flows when mocking is not required.
- Keep robustness tests explicit about expected failure behavior.
Test Isolation¶
Each test should be self-contained and independent:
def test_config_loads_defaults(mock_yaml_config):
service = ConfigService(config_path=mock_yaml_config)
assert service.get("app.name") == "Test Application"
Fixture Usage¶
Use shared fixtures from conftest.py rather than ad-hoc setup:
def test_translation_switch(qt_application, mock_translation_files):
manager = TranslationManager()
manager.load_language_by_code("fr")
assert manager.get_current_language_code() == "fr"
Signal Testing¶
Use wait_for_signal for async signal assertions:
def test_language_changed_signal(qt_application, wait_for_signal):
manager = TranslationManager()
assert wait_for_signal(manager.languageChanged, timeout=1000)
Known Issues¶
Qt Runtime Errors¶
Tests requiring a QApplication must use qt_application or be marked @pytest.mark.qt:
@pytest.mark.qt
def test_widget_renders(qt_application):
panel = SettingsPanel()
assert panel is not None
Singleton Behavior¶
EzApplication enforces a singleton pattern. Tests that must create a fresh instance should use the ez_application_cleanup fixture, which calls create_for_testing().
Common Issues¶
| Issue | Solution |
|---|---|
| Qt runtime errors | Verify PySide6 and qt_application fixture |
| Marker not recognized | Check marker names in tests/pytest.ini |
| Missing configs/assets | Re-run ezqt init in project root |
| Import errors | Reinstall package: pip install -e ".[dev]" |
Notes¶
- Test runner streams output in real time (no buffering).
- Some tests still rely on heavy mocks; prefer real integration paths when extending coverage.
- The
robustness/directory is reserved for future edge case suites.