📦

Overview

PyQt Testing Tools is a professional testing toolkit for PyQt desktop applications. Catch UI bugs, verify behavior, analyze test reliability — all integrated with Claude Code workflows.

Key Features

  • Snapshot Testing - Capture and compare UI state over time
  • Visual Regression - Detect unintended layout/styling changes
  • Mutation Testing - Verify UI responds correctly to edge cases
  • Flakiness Detection - Identify non-deterministic test failures
  • Test Recording - Record user interactions, replay as tests
  • Widget Inspection - Deep introspection of Qt widget properties

Installation

# Install via pip
pip install pyqt-testing-tools

# Or with pytest plugin
pip install pyqt-testing-tools[pytest]

Quick Start

# Test with pytest
import pytest
from pyqt_testing import QtTestCase

class TestLoginDialog(QtTestCase):
    def test_login_success(self):
        # Arrange
        dialog = LoginDialog()
        dialog.show()
        
        # Act
        self.qt_type(dialog.username_field, "user@example.com")
        self.qt_type(dialog.password_field, "password123")
        self.qt_click(dialog.login_button)
        
        # Assert
        self.qt_wait_for_dialog(SuccessDialog)
        self.qt_assert_visible(dialog.success_message)
    
    def test_login_snapshot(self):
        # Visual regression test
        dialog = LoginDialog()
        dialog.show()
        
        # Compare with baseline
        self.qt_snapshot_compare(
            widget=dialog,
            baseline="login-dialog-baseline.png"
        )

Use Cases

Automated UI Regression Testing

Catch visual and behavioral regressions automatically. Run tests on every commit to prevent UI bugs from shipping.

End-to-End User Workflows

Script complete user journeys: open app → navigate → interact → verify results. Ensure critical paths always work.

Cross-Platform Testing

Test on multiple OS/Qt versions. Detect platform-specific rendering issues before users report them.

Performance Profiling

Measure UI responsiveness: widget load times, event handling latency, paint performance.

Integration Options

Standalone Library

Use directly in Python scripts:

from pyqt_testing import QtTester

tester = QtTester(app)
tester.click("MainWindow/QPushButton[0]")
tester.snapshot("current-state")

pytest Plugin

Integrate with pytest for test discovery and reporting:

# conftest.py
pytest_plugins = ['pyqt_testing.pytest_plugin']

# test_ui.py
def test_button_click(qt_tester):
    qt_tester.click("my_button")
    assert qt_tester.is_enabled("submit_button")

Claude Code MCP Server

Use via gpyqt-instrument for AI-assisted testing:

"Claude, run the login tests and report any failures"
"Claude, take a snapshot of the settings dialog and compare with baseline"

Testing Patterns

Page Object Pattern

class LoginPage:
    def __init__(self, tester):
        self.tester = tester
        self.username = "LoginDialog/username_field"
        self.password = "LoginDialog/password_field"
        self.submit = "LoginDialog/submit_button"
    
    def login(self, username, password):
        self.tester.type(self.username, username)
        self.tester.type(self.password, password)
        self.tester.click(self.submit)

Data-Driven Testing

@pytest.mark.parametrize("input,expected", [
    ("valid@email.com", True),
    ("invalid-email", False),
    ("", False),
])
def test_email_validation(qt_tester, input, expected):
    qt_tester.type("email_field", input)
    assert qt_tester.is_valid("email_field") == expected

Mutation Testing

def test_error_handling(qt_tester):
    # Inject edge cases
    mutations = ["", None, "x" * 10000, "<script>", "\n\n\n"]
    
    for mutation in mutations:
        qt_tester.type("input_field", mutation)
        # App should not crash
        assert qt_tester.is_running()

Perfect For

  • PyQt6 application developers - Native testing for PyQt apps
  • QA teams - Automated regression and exploratory testing
  • Claude Code users - AI-assisted UI test generation
  • CI/CD pipelines - Headless UI testing on build servers

Architecture

┌──────────────────┐
│   Your PyQt App  │
│                  │
│  ┌────────────┐  │
│  │ Qt Event   │  │
│  │ System     │  │
│  └──────┬─────┘  │
└─────────┼────────┘

    ┌─────▼─────┐
    │  PyQt     │
    │  Testing  │
    │  Tools    │
    └─────┬─────┘

    ┌─────▼─────────┐
    │ Test Runner   │
    │ (pytest/MCP)  │
    └───────────────┘

Tests interact with Qt’s event system to simulate user actions. Widget state is captured via Qt’s introspection APIs.

Best Practices

  1. Use page objects - Encapsulate UI structure, tests reference pages not widgets
  2. Test behavior, not implementation - Assert outcomes, not internal state
  3. Keep tests independent - Each test should run successfully in isolation
  4. Baseline carefully - Visual baselines should represent correct UI, not first snapshot
  5. Run in CI - Use Xvfb (Linux) or virtual displays for headless testing

CI/CD Example

# .github/workflows/test.yml
name: UI Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          sudo apt-get install -y xvfb
          pip install pyqt-testing-tools[pytest]
      
      - name: Run UI tests
        run: xvfb-run pytest tests/ui/

Documentation

Support