Единственный инструмент, который я нашел - QTestLib. К сожалению, его описание весьма скромное:
К сожалению, официальная документация описывает сферический QString в вакууме, в который поместили "Hello world!". Разумеется в этом случае мне совсем не хотелось бросаться в омут с головой и резать по живому. Поэтому для экспериментов придумал простенькое приложение "Подсчет площади квадрата", о котором и пойдет речь дальше.
Как и полагается, начал я с теста.
#include <QtTest/QtTest>
#include "ui_SqAreaForm.h"
class TestGui: public QObject
{
Q_OBJECT
private slots:
void NormalAreaCalc_data()
{
QTest::addColumn<QTestEventList>("events");
QTest::addColumn<QString>("expected");
QTestEventList list1;
list1.addKeyClick('0');
QTest::newRow("zero") << list1 << "0";
QTestEventList list2;
list2.addKeyClicks("10");
QTest::newRow("sq_10") << list2 << "100";
}
void NormalAreaCalc()
{
QFETCH(QTestEventList, events);
QFETCH(QString, expected);
QWidget wdg;
Ui_SqAreaForm frm;
frm.setupUi(&wdg);
events.simulate(frm.side);
QCOMPARE(frm.area->text(), expected);
}
};
QTEST_MAIN(TestGui)
#include "test.moc"
Т.е. я планирую создать в дизайнере ui, в котором есть поле ввода side и QLabel area, и ожидаю, что площадь для квадрата с нулевой стороной - ноль, а для 10 - 100. Пока все просто и практически по туториалу. Теперь добавим класс, который будет управлять этим самим ui.
#include <QWidget>
class Ui_SqAreaForm;
class SqAreaForm : public QWidget
{
Q_OBJECT
public:
SqAreaForm();
~SqAreaForm();
#ifdef _TESTING_
Ui_SqAreaForm* GetUi();
#endif
private:
Ui_SqAreaForm* ui;
};
Помимо типичного кода я добавил еще один метод специально для тестирования. Конечно, неприятно вносить изменения в код ради тестирования, но это изменение минимально и не влияет на работу приложения. Можно обойтись и без добавления этого метода, но тогда придется получать доступ к виджетам через QApplication::allWidgets(), что во много раз сложнее. Существует еще один вариант, когда наша форма предоставляет геттеры и сеттеры, но, как минимум, главному окну такая функциональность точно не нужна.
А теперь самое интересное добавляем слот для реакции на изменение поля side:
void SqAreaForm::OnSqSideChanged(const QString& newSide)
{
int side = newSide.toInt();
int area = side*side;
ui->area->setText(QString::number(area));
}
Что ж - тесты пройдены и это не может не радовать. Но что делать с ui, который мы не можем вернуть через GetUi? Например, диалоговые окна или окна с сообщениями. Для этого поменяем требования к нашему приложению - пусть площадь рассчитывается после нажатия кнопки и результат появляется во всплывающем окне. В этом случае протестировать работу приложения будет немного сложнее:
#include <QtTest/QtTest>
#include "ui_SqAreaForm.h"
#include "SqAreaForm.h"
#include <QApplication>
#include <QMessageBox>
#include <QTimer>
class TestGui: public QObject
{
Q_OBJECT
private slots:
void initTestCase()
{
msgBoxProcessTimer.setInterval(1000);
msgBoxProcessTimer.setSingleShot(true);
QVERIFY(QObject::connect(&msgBoxProcessTimer, SIGNAL(timeout()), this, SLOT(MsgBoxProcess())));
}
void NormalAreaCalc_data()
{
QTest::addColumn<QTestEventList>("events");
QTest::addColumn<QString>("expected");
QTestEventList list1;
list1.addKeyClick('0');
QTest::newRow("zero") << list1 << "Area of the sqare with side 0 is 0";
QTestEventList list2;
list2.addKeyClicks("10");
QTest::newRow("sq_10") << list2 << "Area of the sqare with side 10 is 100";
}
void NormalAreaCalc()
{
QFETCH(QTestEventList, events);
QFETCH(QString, expected);
SqAreaForm frm;
Ui_SqAreaForm* ui = frm.GetUi();
msgBoxProcessTimer.start();
events.simulate(ui->side);
QTest::mouseClick(ui->calc, Qt::LeftButton);
QCOMPARE(msgBoxMessage, expected);
}
void MsgBoxProcess()
{
QMessageBox* msgBox = dynamic_cast<QMessageBox*>(QApplication::activeModalWidget());
if (msgBox != 0)
{
msgBoxMessage = msgBox->text();
msgBox->close();
}
}
private:
QTimer msgBoxProcessTimer;
QString msgBoxMessage;
};
QTEST_MAIN(TestGui)
#include "test.moc"
Идея теста очень проста: т.к. QDialog::exec() блокирует поток выполнения, нам необходимо взвести таймер, чтобы через 1 секунду обработать уже появившееся окно. Решение, на мой взгляд, не самое удачное, т.к. каждый подобный тест занимает не меньше 1 секунды. Можно поступить более мудро и проверять появление модального окна, скажем, каждые 10мс. Для этого меняем интервал таймера и делаем его не одноразовым(setSingleShot(false)).
С помощью двух описанных выше методик можно вполне сносно протестировать типичные сценарии поведения GUI приложений, написанных на Qt.
Исходники примеров тут.
Долго пыталась разобраться с тестами GUI. Спасибо, что потрудились поделиться наработками - с "живыми" примерами разобраться намного проще.
ОтветитьУдалить