GoogleMock имитирует невиртуальные функции

Мы пишем модульные тесты для существующей кодовой базы. Мы используем Google Test / Google Mock для тестов, C++ 11 и Eclipse CDT с компилятором gcc.

Один из наших классов объединяет Boost Socket. Он использовал его как экземпляр, но, к счастью, мы можем изменить существующую базу кода, и я изменил его на указатель и внедрил сокет как зависимость. Итак, я стал издеваться над вызовами сокета, но возникла проблема: функции Boost не виртуальные.

Я нашел документацию, показывающую, как имитировать невиртуальные функции с помощью внедрение высокопроизводительных зависимостей. Однако попытка реализовать его, как показано, не увенчалась успехом. Например, в документе говорится, что нужно «шаблонизировать свой код». Итак, для нашего класса, который использует сокет форсирования, я последовал их примеру. Я вставил:

template <class boost::asio::ip::udp::socket>

Это должно позволить нам вставить наш фиктивный класс вместо конкретного класса Boost. Я пробовал это перед классом и отдельно перед конструктором, принимающим сокет, в заголовке и файле реализации. В каждом месте я получаю массу ошибок, большинство из которых связаны со строками «нет соответствующего вызова функции» при вызове конструктора.

Ясно, что я что-то не так делаю. Кто-нибудь знает где-нибудь полный пример?

Обновлять: По запросу, вот что у нас сейчас есть:

GoogleMock имитирует невиртуальные функции

class PIngester : public IPIngester{
public:
    // this is the method that takes the socket. It is the constructor, and the socket
    // is a default parameter so the existing code will still work. We added the socket 
    // as a parameter specifically for unit testing. If no socket is provided, the 
    // constructor creates one. It only will ever create a concrete Boost
    // Socket, not a mock one.
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<boost::asio::ip::udp::socket> socket = std::unique_ptr<boost::asio::ip::udp::socket>(nullptr));
    ...

Обновление 2

Я определил общий тип класса для шаблона, но это нарушает существующий код. Вот моя текущая версия:

class PIngester : public IPIngester{
public:
    template <class Socket>
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
    ...

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

 error: no matching function for call to ‘foonamespace::PIngester::PIngester(boost::asio::io_service&, foonamespace::Band&)’                                     
          new PIngester(ioService, band));

Это сообщение об ошибке связано с существующим кодом; похоже, он не распознает параметр по умолчанию.

Обновление 3

Я отказался от этого подхода и решил вместо этого написать Boost Socket Wrapper. Оболочка будет содержать фактический экземпляр Socket, а его методы будут прямым переходом к фактическому Socket. Функции оболочки будут виртуальными, а мой фиктивный объект будет унаследован от оболочки. Тогда мой фиктивный объект будет имитировать виртуальные функции оболочки.

Не могли бы вы показать нам, как вы это настроили? Мне template <class boost::asio::ip::udp::socket> кажется, что вы определяете параметр шаблона как часть определения функции (в этом случае вы должны определить имя общего класса вместо boost::asio::ip::udp::socket). Если вы пытаетесь передать параметр шаблона в соответствии с документацией Google Mock, я бы ожидал увидеть что-то вроде TestFunction<boost::asio::ip::udp::socket>().

frslm 18.07.2018 20:04

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

Frecklefoot 18.07.2018 21:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
638
1

Ответы 1

Проблема, как вы заметили, в том, что компилятор не может вывести тот тип, который Socket здесь должен представлять:

class PIngester : public IPIngester{
public:
    template <class Socket>
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
    ...

Если вы попытаетесь создать объект этого класса без указания третьего аргумента (как в new PIngester(ioService, band)), что именно будет Socket?

Теперь нет возможности явно указать какой-либо параметр шаблона при вызове конструктора, поэтому вы не можете сделать что-то вроде new PIngester<boost::asio::ip::udp::socket>(ioService, band), если конструктор шаблонный.


Вот несколько способов (из потенциально многих), чтобы обойти это:

  1. Вы можете создать шаблон для самого класса PIngester:

    template <class Socket>
    class PIngester : public IPIngester{
    public:
        PIngester(boost::asio::io_service& ioService, Band band,
                  std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
        ...
    

    Тогда вызов new PIngester<boost::asio::ip::udp::socket>(ioService, band) (или ваш фиктивный класс вместо boost::asio::ip::udp::socket) будет работать.

  2. Вы можете определить параметр шаблона по умолчанию:

    class PIngester : public IPIngester{
    public:
        template <class Socket = boost::asio::ip::udp::socket>
        PIngester(boost::asio::io_service& ioService, Band band,
                  std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
        ...
    

    Тогда PIngester(ioService, band) всегда будет использовать этот класс Boost по умолчанию, и вам нужно будет явно передать некоторый socket, который представляет собой уникальный указатель на ваш фиктивный класс, если вы хотите использовать его в тестах.

Это отличные предложения, спасибо! Я дам им шанс.

Frecklefoot 19.07.2018 00:00

Пробовал, но как у гидры отрезаю одну голову, появляются еще две. Теперь gcc сообщает, что у меня не может быть типа шаблона в качестве данных члена. Все нормально; Я собираюсь написать оболочку Boost Socket, которая будет иметь виртуальные функции, которые являются прямым переходом к фактическому Boost Socket. Мой фиктивный объект будет наследовать от оболочки и имитировать ее виртуальные функции.

Frecklefoot 19.07.2018 17:22

Неплохая идея; это хороший подход для обработки сторонних классов.

frslm 19.07.2018 17:31

Другие вопросы по теме