Qt测试夹具生成器Skill qt-test-fixture-generator

Qt测试夹具生成器是一个专门为Qt应用程序开发自动化测试框架的工具。它能够生成包含模拟QObject信号与槽、数据驱动测试、GUI测试配置和基准测试的完整测试基础设施。该工具支持单元测试、集成测试和GUI测试,提供CMake集成和覆盖率报告功能,帮助开发者提高代码质量和测试效率。关键词:Qt测试框架、单元测试自动化、QTest夹具生成、GUI测试工具、数据驱动测试、模拟对象、测试覆盖率、CMake集成、C++测试开发、软件质量保障。

测试 0 次安装 0 次浏览 更新于 2/25/2026

name: qt-test-fixture-generator description: 生成包含模拟QObject信号与槽、数据驱动测试和GUI测试配置的Qt测试夹具 allowed-tools: Read, Write, Edit, Bash, Glob, Grep tags: [qt, 测试, 单元测试, 模拟, 夹具]

Qt测试夹具生成器

生成包含模拟QObject信号与槽、数据驱动测试和GUI测试配置的Qt测试夹具。此技能为Qt应用程序创建全面的测试基础设施。

功能

  • 生成基于QTest的测试夹具
  • 创建支持信号/槽的模拟QObjects
  • 使用QFETCH设置数据驱动测试
  • 配置使用QTestLib的GUI测试
  • 为Qt类生成测试替身
  • 设置基准测试
  • 配置测试覆盖率报告
  • 生成CMake测试集成

输入模式

{
  "type": "object",
  "properties": {
    "projectPath": {
      "type": "string",
      "description": "Qt项目路径"
    },
    "classToTest": {
      "type": "string",
      "description": "要生成测试的类名"
    },
    "testType": {
      "enum": ["unit", "integration", "gui", "benchmark"],
      "default": "unit"
    },
    "mockDependencies": {
      "type": "array",
      "items": { "type": "string" },
      "description": "需要模拟的类"
    },
    "dataProviders": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "testName": { "type": "string" },
          "columns": { "type": "array" },
          "rows": { "type": "array" }
        }
      }
    },
    "generateCoverage": {
      "type": "boolean",
      "default": true
    }
  },
  "required": ["projectPath", "classToTest"]
}

输出模式

{
  "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"]
}

生成的测试夹具

// tst_mywidget.cpp
#include <QtTest/QtTest>
#include <QSignalSpy>
#include "mywidget.h"
#include "mockservice.h"

class tst_MyWidget : public QObject
{
    Q_OBJECT

private slots:
    // 测试生命周期
    void initTestCase();      // 所有测试前执行
    void cleanupTestCase();   // 所有测试后执行
    void init();              // 每个测试前执行
    void cleanup();           // 每个测试后执行

    // 单元测试
    void testConstructor();
    void testSetValue();
    void testSetValue_data();  // 数据提供器
    void testSignalEmission();

    // GUI测试
    void testButtonClick();
    void testKeyboardInput();

    // 基准测试
    void benchmarkCalculation();

private:
    MyWidget* m_widget;
    MockService* m_mockService;
};

void tst_MyWidget::initTestCase()
{
    // 一次性设置
    qDebug() << "开始MyWidget测试";
}

void tst_MyWidget::cleanupTestCase()
{
    // 一次性清理
}

void tst_MyWidget::init()
{
    // 为每个测试创建新实例
    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()
{
    // 数据驱动测试设置
    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()
{
    // 获取测试数据
    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测试
    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"));

    // 测试键盘快捷键
    QTest::keyClick(m_widget, Qt::Key_S, Qt::ControlModifier);
    // 验证保存操作已触发
}

void tst_MyWidget::benchmarkCalculation()
{
    QBENCHMARK {
        m_widget->performCalculation();
    }
}

QTEST_MAIN(tst_MyWidget)
#include "tst_mywidget.moc"

模拟对象

// mockservice.h
#include <QObject>

class MockService : public QObject
{
    Q_OBJECT

public:
    explicit MockService(QObject* parent = nullptr) : QObject(parent) {}

    // 跟踪方法调用
    int fetchDataCallCount() const { return m_fetchDataCalls; }
    void resetCalls() { m_fetchDataCalls = 0; }

    // 配置返回值
    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_executable(tst_mywidget
    tst_mywidget.cpp
    mockservice.h
)

target_link_libraries(tst_mywidget PRIVATE
    Qt6::Test
    MyAppLib  # 被测库
)

# 注册到CTest
add_test(NAME tst_mywidget COMMAND tst_mywidget)

# 覆盖率(使用gcov)
if(ENABLE_COVERAGE)
    target_compile_options(tst_mywidget PRIVATE --coverage)
    target_link_options(tst_mywidget PRIVATE --coverage)
endif()

运行测试

# 运行所有测试
ctest --test-dir build

# 运行特定测试
./build/tests/tst_mywidget

# 运行并显示详细输出
./build/tests/tst_mywidget -v1

# 运行特定测试函数
./build/tests/tst_mywidget testSetValue

# 运行数据驱动测试的特定数据行
./build/tests/tst_mywidget testSetValue:positive

# 为CI输出XML
./build/tests/tst_mywidget -o results.xml,xml

最佳实践

  1. 每个测试一个断言:保持测试专注
  2. 使用QSignalSpy:验证信号发射
  3. 使用数据驱动测试:避免代码重复
  4. 模拟依赖项:隔离被测单元
  5. 测试边界情况:边界值、空输入
  6. 清晰命名测试:描述预期行为

相关技能

  • qt-cmake-project-generator - 项目设置
  • desktop-unit-testing 流程 - 测试工作流
  • cross-platform-test-matrix - CI测试

相关代理

  • qt-cpp-specialist - Qt专业知识
  • desktop-test-architect - 测试策略