有没有办法模拟QSqlQuery?

时间:2015-06-18 00:16:25

标签: c++ qt unit-testing gmock

我刚刚发现了gmock,现在我正在重新思考整个编程过程,因为它是",尽我所能添加单元测试。 令我觉得奇怪的是,在这个过程中,QSql模块明显是我们代码的外部依赖,并没有给开发人员提供模拟其内部的工具。我能用这个模块想到的最好的东西是内存数据库,它比简单的模拟更难实现,甚至不可能(考虑使用内存数据库伪造oracle包)

现在,对我来说,这不是一个问题,前一段时间我们已经切换到继承自虚拟接口的本地增长的ocilib包装器(因此,很容易模拟)。但是真的,当你使用Qt自己的QSql模块时,有没有办法嘲笑?或者更确切地说 - Qt是一个(非常好的)框架,他们真的没有为这些用例提供自动化,或者我错过了什么?

UPD1: 关于问题重要性的小更新:

我的代码与Oracle SQL查询非常交错,对于某些其他人的代码而言。当外部依赖(也是大量开发)有时会提供不正确的数据时,单元测试这样的代码实际上是不可能的。当您的单元测试中断时,您希望它是您的代码,而不是Oracle。这就是我问原问题的原因。如果存在/存在使用qsqlquery接口半容易地模拟依赖关系的方法,则可以使用QSql编写代码的单元测试。

UPD2:虽然在进一步考虑之后,我不得不承认,通过更好的代码设计(OO而不是某些地方的免费功能)和更好的实体分离可以避免这个问题。因此,UPD1中的几乎不可能并不合理。虽然这并不能使原始问题变得不那么重要。例如,当你负责维护遗留代码时,模拟QtSql是将测试引入系统的唯一现实方法。

2 个答案:

答案 0 :(得分:1)

如果您只想将内存中的SQL数据库用作QtSQL的模拟后端,可以考虑使用SQLite

  

SQLite是一个进程内库,它实现了一个独立的,无服务器,零配置的事务SQL数据库引擎。 SQLite的代码属于公共领域,因此可以免费用于任何目的,商业或私人。 SQLite是世界上部署最广泛的数据库,其应用程序数量超出我们的数量,包括几个备受瞩目的项目。

在QtSQL调用背后使用真正的SQL解释器的优点是,您可以验证传入的SQL语法,以及查询是否实际返回预期结果。

如果您关注的是测试执行Oracle SQL特定功能的SQL查询,那么没有其他方法可以知道您正在使用这些功能而无需针对真正的Oracle SQL服务器进行测试。

答案 1 :(得分:1)

Zeks,IMO你有两种模拟Qt Sql类的方法:

  1. 对Qt Sql类进行子类化;
  2. 围绕Qt Sql类的包装器并通过接口传递它们。
  3. 方法#1:

    一般来说,这很痛苦。首先,如果要模拟QSqlQuery,则必须为QSqlResult,QSqlDriver和QSqlQuery本身创建子类。然后,另一个痛苦进入游戏,你必须设置前提条件 - 例如:你希望你的sql在调用exec()函数时返回true,为此,你的QSqlDriver的子类必须返回:

    class QSqlDriverTest : public QSqlDriver
    {
       ...
       virtual bool isOpen() const { return true; }
       virtual void setOpenError(bool e) { QSqlDriver::setOpenError(false); }
       ...
    };
    

    这只是一个例子。成功调用next()函数还有更多的前提条件。要找到它们,您总是需要查看qt源代码。所以它完全取决于qt。这种方法失败的原因是:

    • 这并不容易 - 单元测试必须简单;
    • 你仍然有qt依赖;

    方法#2:

    我认为这是模拟查询的最佳和最明确的方式,但您需要准备代码。您创建一个接口ISQLQuery,它具有与QSqlQuery相同的功能(对于QSqlDriver和QSqlResult也是如此)。像这样:

    class ISQLQuery   // interface wrapper for QSqlQuery
    {
    public:
       ~ISQLQuery(){}
       ...
       virtual bool exec() = 0;
       virtual QVariant value(int i) const = 0;
       virtual const ISQLDriver * driver() const = 0;
       ...
    };
    
    class ISQLDriver   // interface wrapper for QSqlDriver
    {
    public:
       ~ISQLDriver(){}
       ...
       virtual bool subscribeToNotification(const QString & name) = 0;
       ...
    };
    

    然后你创建真正的实现(这只是草案的想法):

    class SQLDriver : public ISQLDriver
    {
    public:
       SQLDriver(const QSqlDriver * driver) : mpDriver(driver){}
       ...
       virtual bool subscribeToNotification(const QString & name) 
          { return mpDriver->subscribeToNotification(name); }
       ...
    private:
       const QSqlDriver * mpDriver;
    };
    
    class SQLQuery : public ISQLQuery
    {
    public:
       SQLQuery(): mDriver(mQuery->driver){}
       ...
       virtual bool exec() { return mQuery.exec(); }
       virtual QVariant value(int i) const { return mQuery.value(i); }
       virtual const SQLDriver * driver() const { return &mDriver; }
       ...
    private:
       QSqlQuery mQuery;
       SQLDriver mDriver;
       ...
    };
    

    有一个例子,当创建和实现所有接口时,如何使用新的sql类:

    // some function
    {
       ...
       SQLQuery query = SQLFactory::createSQLQuery();   // here you can put your mocks
       query.prepare("DROP TABLE table_hell;");
       query.exec();
       ...
    }
    

    我已经向你展示了没有所有细节的主要想法,否则帖子可能变得庞大。我希望你会发现我的解释很有用。

    问候。