Подтвердить что ты не робот

Есть ли способ издеваться над QSqlQuery?

Недавно я обнаружил gmock, и теперь я продолжаю "переосмысливать весь процесс программирования как есть", добавляя модульные тесты, где только можно. Одна вещь, которая показалась мне странной в этом процессе, заключается в том, что модуль QSql явно является внешней зависимостью от нашего кода, не дает разработчикам инструментов издеваться над его внутренними компонентами. Самое лучшее, что я могу придумать с помощью этого модуля, - это DB в памяти, который намного сложнее реализовать, чем простой макет и даже не всегда возможен (подумайте о подделке пакетов Oracle с внутренней памятью)

Теперь для меня это не совсем проблема. Некоторое время назад мы перешли на домашнюю оболочку ocilib, которая наследуется от виртуального интерфейса (и, таким образом, легко макетируется). Но на самом деле, разве нет способа издеваться над вами, когда вы используете Qt-модуль Qt? Вернее - Qt является (действительно хорошей) структурой, действительно ли они не обеспечивают автоматизацию для таких случаев использования или я что-то не хватает?

UPD1: Небольшое обновление важности вопроса:

Мой код ОЧЕНЬ сильно чередуется с запросами Oracle SQL, как, наверняка, кода многих других людей. Единичное тестирование такого кода практически невозможно, когда внешняя зависимость, которая также сильно развивается, иногда поставляет неверные данные. Когда ваш unit test ломается, вы хотите, чтобы это был ваш код, который виноват, а не Oracle. Вот почему я задал оригинальный вопрос. Если существует/существует способ полупросто издеваться над зависимостью с использованием интерфейса qsqlquery, тогда становится возможным выполнение модульных тестов для кода с использованием QSql.

UPD2: Хотя, после дальнейшего рассмотрения, я должен признать, что проблему можно было бы избежать при улучшении дизайна кода (вместо OO вместо свободных функций в некоторых местах) и лучшего разделения объектов. Таким образом, практически невозможно в UPD1 не было действительно оправдано. Хотя это действительно не делает вопрос оригинальным менее важным. Когда вам поручено поддерживать устаревший код, например, насмешка QtSql является единственным реалистичным способом введения тестов в систему.

4b9b3361

Ответ 1

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

SQLite - это встроенная библиотека, которая реализует автономный, безсерверный, нулевой конфигурации, транзакционный механизм СУБД SQL. Код для SQLite находится в общественном достоянии и, следовательно, свободен для использования в любых целях, коммерческих или частных. SQLite - это наиболее широко распространенная база данных в мире с большим количеством приложений, чем мы можем рассчитывать, в том числе несколько высокопрофильных проектов.

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

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

Ответ 2

Zeks, у IMO у вас есть 2 подхода к макету классов Qt Sql:

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

Подход №1:

Вообще это боль. Во-первых, если вы хотите издеваться над QSqlQuery, вам нужно создать подклассы для QSqlResult, QSqlDriver и QSqlQuery. Затем в игру приходит еще одна боль, вы должны установить предварительные условия - например: вы хотите, чтобы ваш sql возвращал true при вызове функции exec(), для этой цели ваш подкласс 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();
   ...
}

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

С уважением.