Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate Qt Test fixtures with mock QObject signals and slots, data-driven tests, and GUI testing setup
.claude/skills/a5c-ai-qt-test-fixture-generator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 151% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 27% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 80% | 0% |
Generate Qt Test fixtures with mock QObject signals and slots, data-driven tests, and GUI testing setup. This skill creates comprehensive test infrastructure for Qt applications.
json{ "type": "object", "properties": { "projectPath": { "type": "string", "description": "Path to the Qt project" }, "classToTest": { "type": "string", "description": "Name of the class to generate tests for" }, "testType": { "enum": ["unit", "integration", "gui", "benchmark"], "default": "unit" }, "mockDependencies": { "type": "array", "items": { "type": "string" }, "description": "Classes to mock" }, "dataProviders": { "type": "array", "items": { "type": "object", "properties": { "testName": { "type": "string" }, "columns": { "type": "array" }, "rows": { "type": "array" } } } }, "generateCoverage": { "type": "boolean", "default": true } }, "required": ["projectPath", "classToTest"] }
json{ "type": "object", "properties": { "success": { "type": "boolean" }, "files": { "type": "array", "items": { "type": "object", "properties": { "path": { "type": "string" }, "type": { "enum": ["test", "mock", "cmake"] } } } }, "runCommand": { "type": "string" } }, "required": ["success"] }
cpp// tst_mywidget.cpp #include <QtTest/QtTest> #include <QSignalSpy> #include "mywidget.h" #include "mockservice.h" class tst_MyWidget : public QObject { Q_OBJECT private slots: // Test lifecycle void initTestCase(); // Before all tests void cleanupTestCase(); // After all tests void init(); // Before each test void cleanup(); // After each test // Unit tests void testConstructor(); void testSetValue(); void testSetValue_data(); // Data provider void testSignalEmission(); // GUI tests void testButtonClick(); void testKeyboardInput(); // Benchmark void benchmarkCalculation(); private: MyWidget* m_widget; MockService* m_mockService; }; void tst_MyWidget::initTestCase() { // One-time setup qDebug() << "Starting MyWidget tests"; } void tst_MyWidget::cleanupTestCase() { // One-time cleanup } void tst_MyWidget::init() { // Create fresh instances for each test m_mockService = new MockService(this); m_widget = new MyWidget(m_mockService); } void tst_MyWidget::cleanup() { delete m_widget; delete m_mockService; m_widget = nullptr; m_mockService = nullptr; } void tst_MyWidget::testConstructor() { QVERIFY(m_widget != nullptr); QCOMPARE(m_widget->value(), 0); QVERIFY(m_widget->isEnabled()); } void tst_MyWidget::testSetValue_data() { // Data-driven test setup QTest::addColumn<int>("input"); QTest::addColumn<int>("expected"); QTest::addColumn<bool>("shouldEmitSignal"); QTest::newRow("zero") << 0 << 0 << false; QTest::newRow("positive") << 42 << 42 << true; QTest::newRow("negative") << -10 << -10 << true; QTest::newRow("max") << INT_MAX << INT_MAX << true; } void tst_MyWidget::testSetValue() { // Fetch test data QFETCH(int, input); QFETCH(int, expected); QFETCH(bool, shouldEmitSignal); QSignalSpy spy(m_widget, &MyWidget::valueChanged); m_widget->setValue(input); QCOMPARE(m_widget->value(), expected); QCOMPARE(spy.count(), shouldEmitSignal ? 1 : 0); } void tst_MyWidget::testSignalEmission() { QSignalSpy spy(m_widget, &MyWidget::valueChanged); QVERIFY(spy.isValid()); m_widget->setValue(100); QCOMPARE(spy.count(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 100); } void tst_MyWidget::testButtonClick() { // GUI testing QPushButton* button = m_widget->findChild<QPushButton*>("submitButton"); QVERIFY(button != nullptr); QSignalSpy spy(m_widget, &MyWidget::submitted); QTest::mouseClick(button, Qt::LeftButton); QCOMPARE(spy.count(), 1); } void tst_MyWidget::testKeyboardInput() { QLineEdit* input = m_widget->findChild<QLineEdit*>("nameInput"); QVERIFY(input != nullptr); input->setFocus(); QTest::keyClicks(input, "Hello World"); QCOMPARE(input->text(), QString("Hello World")); // Test keyboard shortcut QTest::keyClick(m_widget, Qt::Key_S, Qt::ControlModifier); // Verify save action triggered } void tst_MyWidget::benchmarkCalculation() { QBENCHMARK { m_widget->performCalculation(); } } QTEST_MAIN(tst_MyWidget) #include "tst_mywidget.moc"
cpp// mockservice.h #include <QObject> class MockService : public QObject { Q_OBJECT public: explicit MockService(QObject* parent = nullptr) : QObject(parent) {} // Track method calls int fetchDataCallCount() const { return m_fetchDataCalls; } void resetCalls() { m_fetchDataCalls = 0; } // Configure return values void setFetchDataResult(const QString& result) { m_fetchDataResult = result; } public slots: QString fetchData(int id) { m_fetchDataCalls++; m_lastFetchId = id; emit dataRequested(id); return m_fetchDataResult; } signals: void dataRequested(int id); public: int lastFetchId() const { return m_lastFetchId; } private: int m_fetchDataCalls = 0; int m_lastFetchId = -1; QString m_fetchDataResult = "mock result"; };
cmake# tests/CMakeLists.txt find_package(Qt6 REQUIRED COMPONENTS Test) enable_testing() # Add test executable add_executable(tst_mywidget tst_mywidget.cpp mockservice.h ) target_link_libraries(tst_mywidget PRIVATE Qt6::Test MyAppLib # Library under test ) # Register with CTest add_test(NAME tst_mywidget COMMAND tst_mywidget) # Coverage (with gcov) if(ENABLE_COVERAGE) target_compile_options(tst_mywidget PRIVATE --coverage) target_link_options(tst_mywidget PRIVATE --coverage) endif()
bash# Run all tests ctest --test-dir build # Run specific test ./build/tests/tst_mywidget # Run with verbose output ./build/tests/tst_mywidget -v1 # Run specific test function ./build/tests/tst_mywidget testSetValue # Run data-driven test with specific data row ./build/tests/tst_mywidget testSetValue:positive # Output XML for CI ./build/tests/tst_mywidget -o results.xml,xml
qt-cmake-project-generator - Project setupdesktop-unit-testing process - Testing workflowcross-platform-test-matrix - CI testingqt-cpp-specialist - Qt expertisedesktop-test-architect - Test strategy| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 12,758 | 14,553 | +14% | 1 | 1 | 0% | 2,401 | 6,037 | +151% | 0 | 0 | — |
case-02 | fail→pass | 18,798 | 14,375 | -24% | 1 | 1 | 0% | 4,272 | 5,809 | +36% | 0 | 0 | — |
case-03 | fail→fail | 13,301 | 8,128 | -39% | 1 | 1 | 0% | 3,220 | 4,076 | +27% | 0 | 0 | — |
case-04 | pass→pass | 7,127 | 4,893 | -31% | 1 | 1 | 0% | 1,659 | 3,407 | +105% | 0 | 0 | — |
case-05 | pass→pass | 9,058 | 12,840 | +42% | 1 | 1 | 0% | 1,967 | 5,214 | +165% | 0 | 0 | — |
case-06 | pass→fail | 15,311 | 22,411 | +46% | 1 | 1 | 0% | 3,530 | 8,159 | +131% | 0 | 0 | — |
case-07 | fail→pass | 21,395 | 15,470 | -28% | 1 | 1 | 0% | 3,720 | 6,045 | +63% | 0 | 0 | — |
case-08 | fail→pass | 14,219 | 13,473 | -5% | 1 | 1 | 0% | 4,307 | 5,469 | +27% | 0 | 0 | — |
case-09 | fail→pass | 11,113 | 9,978 | -10% | 1 | 1 | 0% | 2,563 | 4,624 | +80% | 0 | 0 | — |
case-10 | fail→pass | 26,336 | 12,234 | -54% | 1 | 1 | 0% | 5,022 | 5,126 | +2% | 0 | 0 | — |
case-11 | pass→pass | 11,104 | 8,258 | -26% | 1 | 1 | 0% | 2,833 | 4,291 | +51% | 0 | 0 | — |
case-12 | pass→pass | 8,463 | 7,548 | -11% | 1 | 1 | 0% | 1,970 | 4,183 | +112% | 0 | 0 | — |
case-13 | fail→pass | 8,836 | 10,399 | +18% | 1 | 1 | 0% | 2,071 | 4,871 | +135% | 0 | 0 | — |
case-14 | pass→pass | 10,432 | 13,943 | +34% | 1 | 1 | 0% | 2,336 | 5,534 | +137% | 0 | 0 | — |
case-15 | fail→pass | 23,106 | 12,017 | -48% | 1 | 1 | 0% | 6,047 | 4,315 | -29% | 0 | 0 | — |
case-16 | fail→pass | 7,695 | 11,406 | +48% | 1 | 1 | 0% | 1,617 | 5,123 | +217% | 0 | 0 | — |
case-17 | fail→pass | 10,917 | 11,142 | +2% | 1 | 1 | 0% | 2,704 | 4,974 | +84% | 0 | 0 | — |
case-18 | fail→pass | 11,310 | 13,690 | +21% | 1 | 1 | 0% | 2,721 | 5,520 | +103% | 0 | 0 | — |
case-19 | fail→pass | 14,210 | 11,732 | -17% | 1 | 1 | 0% | 3,347 | 5,292 | +58% | 0 | 0 | — |
case-20 | fail→pass | 22,218 | 11,000 | -50% | 1 | 1 | 0% | 4,134 | 5,055 | +22% | 0 | 0 | — |
case-21 | fail→pass | 8,639 | 9,948 | +15% | 1 | 1 | 0% | 1,763 | 4,795 | +172% | 0 | 0 | — |
case-22 | fail→pass | 9,577 | 9,568 | -0% | 1 | 1 | 0% | 2,357 | 4,641 | +97% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +64 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.