GTest: имитирующая невиртуальную функцию-член

#c #mocking #googletest

#c #насмешливый #googletest

Вопрос:

Рассмотрим следующий фрагмент кода:

источник.hpp

 class tracker
{
public:
  static trackeramp; get_instance()
  {
    static tracker instance;
    return instance;
  }

  tracker(const trackeramp;) = delete;
  trackeramp; operator=(const trackeramp;) = delete;

private:
  tracker()
  {
     _ip_count = settings::get_instance().get_ips();
     // ...
  }

private:
  int _ip_count;
};
  

test.cpp

 #include "source.hpp"
#include "settings.hpp"

#include "gtest/gtest.h"
#include "gmock/gmock.h"

struct MockSettings
{
  MOCK_CONST_METHOD0(get_ips, int());
};

TEST(tracker, _)
{
  // Need to mock settings::get_instance().get_ips() function here
  trackeramp; inst = tracker::get_instance();
}

int main(int argc, char** argv)
{
  ::testing::InitGoogleMock(amp;argc, argv);
  return RUN_ALL_TESTS();
}
  

Как вы можете видеть в test.cpp получаемом экземпляре tracker , следовательно settings::get_instance().get_ips() , вызывается функция. На самом деле, мне не нужно вызывать последнюю функцию, вместо этого я хотел бы вернуть, например, 3 . Как я могу сделать это, учитывая, что get_ips() это не виртуальная функция. Я не хочу менять исходные коды, если это возможно. Также я прочитал следующую документацию https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md , но не смог достичь желаемого результата.

Ответ №1:

Похоже, что проблема, с которой вы можете столкнуться здесь, заключается в том, что вы пытаетесь имитировать класс settings , но у конструктора tracker нет возможности фактически использовать ваш класс mock settings, потому что во время компиляции исправлено прямое обращение к settings .

В кулинарной книге действительно объясняется, как это решить, в разделе «Имитирующие невиртуальные методы«: сначала вы создаете свой макет класса, затем предоставляете некоторые средства внедрения зависимостей, которые позволяют вам выбирать между производственной и макетной версиями класса во время компиляции. Одним из способов сделать это было бы сделать tracker шаблоном класса, параметризованным с помощью класса settings чем-то вроде:

 template <typename settings_class> class tracker_template
{
public:
  static tracker_template<settings_class>amp; get_instance()
  {
    static tracker_template<settings_class> instance;
    return instance;
  }

// ...

private:
  tracker_template()
  {
     _ip_count = settings_class::get_instance().get_ips();
     // ...
  }

// ...
};
  

После чего вы могли бы, например. using tracker = tracker_template<settings>; продолжать использовать tracker в вашем производственном коде и вместо этого использовать tracker_template<MockSettings> в вашем тестовом коде.

Вероятно, нет способа решить это без изменения вашего кода.