pImpl 惯用语和可测试性Qt Creator - GUI 应用程序测试

问题描述

已经有一些关于 pImpl 习语及其用法对应用可测试性的意义的阅读资料(例如,请参阅:The pImpl idiom and Testability)。虽然最常见的“官方”答案总是类似于:“隐藏在 pImpl 构造后面的所有内容都不是为了让您测试。假设代码的一部分是合理的,然后对可用部分进行黑盒测试代码”,当隐藏在不透明指针后面的是 GUI(或其他有价值的对象)时,我无法找到一种方法(或常见做法)让单元测试与不透明指针共存。稍后与代码的“可用”部分(即调用方法)进行交互的用户类型的交互。

特别是,我正在考虑 Qt 隐藏使用 Qt Creator 设计的小部件/对话框的实现的不透明指针。假设我有一个带有文本显示和“新建”按钮的主窗口。如果我按下按钮,则会出现一个带有 lineEdit 和 buttonBox(确定/取消)的新对话框。如果我在 lineEdit 中写入一些文本并按“确定”,则新对话框将关闭,文本将显示在主窗口的文本显示中。如果我在新对话框中按“取消”(不管 lineEdit 中的文本如何),对话框将关闭,没有进一步的影响。

上述简单程序的设计如下:

//mainwindow.h
#pragma once

#include <QMainWindow>
...

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

    public:
        newDialog* newdialog;

        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();

    private slots:
        void on_buttonNew_clicked();

    private:
            Ui::MainWindow *ui;
};

ui 对象(命名空间 MainWindow 中名为 Ui 的类)的实际实现可以在头文件 ui_mainwindow.h 中找到,Qt 会根据该头文件自动生成XML 文件,但这对当前问题并不重要。

MainWindow文件提供了一个专用槽,用于调用新的对话框类并收集用户输入的文本,以防用户单击“确定”:

//mainwindow.cpp

...

void MainWindow::on_buttonNew_clicked()
{

    int res;
    this->newdialog = new newDialog(this);
    res = newdialog->exec();

    if (res == QDialog::Rejected)
    {
        // do nothing
        return;
    }

    QString newDialogText = newdialog->get_text();

    // set newDialog text in mainwindow text display
}

同样,我们有 newDialog文件和源文件

//newdialog.cpp
#include "newdialog.h"
#include "ui_newdialog.h"

newDialog::newDialog(QWidget *parent) : QDialog(parent),ui(new Ui::newDialog)
{
    ui->setupUi(this);
}

newDialog::~newDialog()
{
    delete ui;
}

void newDialog::on_buttonBox_rejected()
{
    reject();
}

void newDialog::on_buttonBox_accepted()
{
    accept();
}

QString newDialog::get_text()
{
    QString text = ui->lineEdit->text();
    return text;
}


//newdialog.h
#pragma once

#include <QDialog>

namespace Ui {
class newDialog;
}

class newDialog : public QDialog
{
    Q_OBJECT


public:
    explicit newDialog(QWidget *parent = nullptr);
    ~newDialog();

    QString get_text();

private slots:
    void on_buttonBox_rejected();
    void on_buttonBox_accepted();

private:
    Ui::newIssue *ui;    
};

最后,newDialog 类的(隐藏)实现,在头文件 ui_newdialog.h 中:

//ui_newdialog.h
#ifndef UI_NEWDIALOG_H
#define UI_NEWDIALOG_H

#include <QtCore/QVariant>
...

QT_BEGIN_NAMESPACE

class Ui_newDialog
{
    
public:
    QLineEdit *lineedit;
    QDialogButtonBox *buttonBox;

    void setupUi(QDialog *newDialog)
    {
        ...
    }

    void retranslateUi(QDialog *newDialog)
    {
        ...
    }

};

namespace Ui {
    class newDialog: public Ui_newDialog {};
}

QT_END_NAMESPACE

#endif

现在,我似乎无法理解以下内容。我想测试 MainWindow::on_buttonNew_clicked() 方法。在我的测试类的头文件中,我声明了一个 MainWindow* 实例,并且由于 MainWindow 类将 newDialog* newdialog; 声明为公共成员,我可以从我的测试中访问新的对话框公共方法类(即只有 newDialog::get_text())。

尽管如此,在测试期间,我想自动向新对话框提供一些虚拟文本,以查看是否例如它被正确解析到主窗口 newDialogText 变量中。为此,QTest 提供了一些功能,例如QTest::mouseClick(...,Qt::LeftButton),但是:

  • 如果对 lineeditbuttonBox 的实际访问受 pImpl 保护,我应该如何使用它?
  • 有没有什么方法可以在不撤消 pImpl 的情况下执行此测试?
  • “你不应该测试这个”是否仍然适用于这个案例?
  • 我的软件设计是否从根本上是错误的,这是因为我遇到了这种情况?

任何帮助将不胜感激。提前致谢。

编辑 1:

friend 声明添加newDialog 类没有帮助。这是:

//ui_newdialog.h

...

namespace Ui {
    class newDialog: public Ui_newDialog
    {
        friend class TestMainWindow;
    };
}

代替:

//ui_newdialog.h

...

namespace Ui {
    class newDialog: public Ui_newDialog {};
}

其中 TestMainWindow 显然是将运行测试的类。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...