Is there a way to mock QSqlQuery?

Refresh

December 2018

Views

894 time

19

I just recently discovered gmock and am now in progress of "rethinking the whole programming process as it is", adding unit tests wherever I can. One thing that struck me as weird in the process is that QSql module clearly being an external dependency to our code, doesn't give developers tools to mock its internals. The best thing I can think of with this module is in-memory DB which is way harder to implement than a simple mock and is not even always possible (consider faking oracle packages with in-memory DB)

Now, for me, that is not exactly a problem, a while ago we have switched to home grown ocilib wrapper that inherits from virtual interface (and is, thus, easily mockable). But really, is there no way to mock when you use Qt's own QSql module? Or rather - Qt being a (really good) framework, do they really provide no automation for such use cases or am I missing something?

UPD1: A small update on the importance of the question:

My code is VERY heavily interleaved with Oracle SQL queries as, for certain, code of a lot of other people. Unit testing such a code is virtually impossible when an external dependency, which is also heavily in development, sometimes supplies incorrect data. When your unit test breaks, you want it to be your code that is at fault, not Oracle. Which is why I asked the original question. If there exists/existed a way to semi-easily mock the dependency using qsqlquery interface then writing unit tests for code using QSql becomes possible.

UPD2: Although, after further consideration, I have to admit that the problem could be avoided with better code design (OO instead of free functions at some places) and better entity separation. So, virtually impossible in UPD1 was not really justified. Though this doesn't really make original question less important. When you are tasked maintaining legacy code, for example, mocking QtSql is the only realistic way of introducing tests to the system.

2 answers

1

Если вы просто хотите, в базе данных SQL памяти для использования в качестве притворного бэкэнда для QtSql, вы можете рассмотреть возможность использования SQLite .

SQLite является библиотекой в ​​процессе, который реализует автономный, Serverless, нулевой конфигурации, транзакционной SQL движок базы данных. Код для SQLite находится в свободном доступе и, таким образом, бесплатно для использования в любых целях, коммерческих или частных. SQLite является наиболее широко распространенными в мире базы данных с большим количеством приложений, чем мы можем рассчитывать, в том числе нескольких громких проектов.

Преимущество использования настоящего переводчика SQL позади QtSql вызывает то, что вы можете проверить SQL синтаксис принятую в, и является ли запрос фактически возвращает ожидаемый результат.

Если ваша проблема тестирования запросов SQL, которые осуществляют Oracle SQL специфические функции, то нет никакого другого способа знать, что вы используете эти функции корректно без тестирования с реальным сервером Oracle SQL.

jxh
1

Зэки , ИМО у вас есть 2 подхода издеваться классов Qt Sql:

  1. Подклассов классы Qt Sql;
  2. Обертка вокруг классов Qt Sql и передавая их через интерфейсы.

Подход № 1:

Как правило, это боль. Во-первых, если вы хотите, чтобы дразнить QSqlQuery вы должны создать подклассы для QSqlResult, QSqlDriver и сам QSqlQuery. Затем еще одна боли приходит в игру, вы должны установить предварительные условия - например, вы хотите, чтобы ваш SQL вернуть истинный при вызове функции EXEC (), для этой цели, ваш подкласс QSqlDriver должен вернуть:

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

Вот только один пример. Есть еще больше предпосылок для вызова следующей функции () успешно. Чтобы узнать их, вы всегда должны смотреть в кварт исходного кода. Таким образом, это полностью зависит от 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();
   ...
}

Я показал вам основную идею без всех деталей в противном случае сообщение может стать огромным. Я надеюсь, что вы найдете мое объяснение полезным.

С уважением.