有没有办法模拟QSqlQuery

Is there a way to mock QSqlQuery?

本文关键字:QSqlQuery 模拟 有没有      更新时间:2023-10-16

我最近才发现了gmock,现在正在"重新思考整个编程过程",尽可能地添加单元测试。在这个过程中,让我感到奇怪的一件事是,QSql 模块显然是我们代码的外部依赖项,没有为开发人员提供模拟其内部的工具。我能想到的最好的东西是内存数据库,它比简单的模拟更难实现,甚至并不总是可能的(考虑使用内存数据库伪造预言机包)

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

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

我的代码与Oracle SQL查询交错在一起,当然,这是许多其他人的代码。 当外部依赖项(也在大量开发中)有时会提供不正确的数据时,对此类代码进行单元测试几乎是不可能的。当你的单元测试中断时,你希望出错的是你的代码,而不是 Oracle。这就是我问原始问题的原因。如果存在/存在一种使用 qsqlquery 接口半轻松模拟依赖关系的方法,那么使用 QSql 为代码编写单元测试成为可能。

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

Zeks,IMO 你有 2 种方法来模拟 Qt Sql 类:

  1. 子类化Qt Sql类;
  2. 围绕Qt Sql类的包装器并通过接口传递它们。

方法#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();
   ...
}

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

问候。

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

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

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

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