QSignalSpy — инструмент для тестирования сигналов в Qt

Автор: | 20 июня, 2025

QSignalSpy — мощный класс из модуля QtTest, который используется для отслеживания сигналов во время юнит-тестирования. Он позволяет проверить, был ли сигнал отправлен, сколько раз, с какими аргументами и в каком порядке.


Назначение

Когда вы пишете юнит-тесты в Qt, часто возникает необходимость убедиться, что сигнал был сгенерирован, например:

  • пользователь нажал кнопку;
  • завершилась асинхронная операция;
  • произошла ошибка;
  • объект изменил состояние.

В таких случаях QSignalSpy заменяет ручное подключение сигналов к слотам и анализирует информацию о них автоматически.


Подключение

#include <QSignalSpy>
#include <QtTest>

Также потребуется:

QT += testlib

Простой пример

Допустим, у нас есть класс:

class MyObject : public QObject {
    Q_OBJECT
public:
    void doSomething() {
        emit finished(42);
    }

signals:
    void finished(int value);
};

Теперь напишем тест с использованием QSignalSpy:

void testSignal() {
    MyObject obj;

    QSignalSpy spy(&obj, SIGNAL(finished(int)));

    obj.doSomething();

    QCOMPARE(spy.count(), 1); // сигнал должен быть вызван один раз

    QList<QVariant> arguments = spy.takeFirst();
    QCOMPARE(arguments.at(0).toInt(), 42); // проверка переданного аргумента
}

Методы класса QSignalSpy

МетодОписание
spy.count()Количество вызовов сигнала
spy.wait(ms)Ожидание сигнала (удобно для асинхронных операций)
spy.takeFirst()Получить список аргументов первого вызова
spy.at(index)Получить список аргументов по индексу
spy.isValid()Проверка, правильно ли создан шпион

Пример с асинхронностью

QSignalSpy spy(&object, &SomeClass::finished);

// запускаем асинхронную операцию
object.startWork();

// ждём до 2 секунд, пока не будет вызван сигнал
QVERIFY(spy.wait(2000));

QCOMPARE(spy.count(), 1);

Поддержка перегруженных сигналов

Если сигнал перегружен (имеет несколько вариантов), используйте функциональный указатель:

QSignalSpy spy(&object, SIGNAL(signalName(Type)));

Обработка аргументов

QList<QVariant> args = spy.at(0);
QString name = args.at(0).toString();
int id = args.at(1).toInt();

Практика: пример с QPushButton

QPushButton button;
QSignalSpy clickedSpy(&button, &QPushButton::clicked);

QTest::mouseClick(&button, Qt::LeftButton);

QCOMPARE(clickedSpy.count(), 1);

Использование в рамках QTest

class TestMyClass : public QObject {
    Q_OBJECT

private slots:
    void testFinishedSignal();
};

void TestMyClass::testFinishedSignal() {
    MyObject obj;
    QSignalSpy spy(&obj, SIGNAL(finished(int)));

    obj.doSomething();

    QCOMPARE(spy.count(), 1);
    QList<QVariant> args = spy.takeFirst();
    QCOMPARE(args.at(0).toInt(), 42);
}

Потенциальные недостатки

  • QSignalSpy работает только в пределах одного потока, где расположен объект.
  • В случае с асинхронными сигналами может потребоваться QTRY_COMPARE или wait().
  • Проверка типов аргументов основана на QVariant, что может привести к неявным приведением.

Заключение

QSignalSpy — незаменимый инструмент для тестирования событийной архитектуры в Qt. Он помогает:

  • Проверить, вызываются ли сигналы;
  • Проверить передаваемые параметры;
  • Проверить асинхронные реакции и события в GUI.