Gtest и gmock в приведенном ниже фрагменте кода, хотя ошибка

#c 11 #googletest #googlemock

#c 11 #googletest #googlemock

Вопрос:

Как добиться gtest или gmock для частной и защищенной функции-члена. Я новичок в gtest и gmock. Ниже приведен код, для которого мне нужно выполнить gtest или gmock вместе с моей попыткой.

 constexpr static char _session[]{"S_ID"};

typedef struct {
    int  session;

} Session;

typedef std::function<void(const Session amp;)> SessionCallback_t;

class Service : public ParentService {
public:
    Service();

    void registerCallback(const SessionCallback_t amp; callback);

protected:
    virtual void notifyHandler(const Json::Value amp; data) override;
    virtual void notifyState();
private:
   
    Session mSession;
    SessionCallback_t mCallback;

    void jsonParse(const Json::Value amp; json_data);
};
  

Моя попытка, которая не компилируется

 class TestService : public Service {
        public:
        TestService(): Service() {
        }
        bool registerCallback(const SessionCallback_t amp; cb) {
           // how to achive this?
              
        }


};


class MyTestService : public ::testing::Test {
    
protected:
  virtual void SetUp() {
  }

  virtual void TearDown() {
    
  }
};

 

TEST_F(MyTestService , registerCallbackTest) {
      TestService service;
       EXPECT_TRUE(service.registerCallback(SessionCallback_t));
    
}   
  

Я застрял с нижеприведенным интерфейсом

1.registerCallback()

2.notifyHandler()

3.notifyState()

4.jsonParse()

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

Ответ №1:

Добро пожаловать в Stack Overflow!

Во-первых, позвольте мне порекомендовать этот недавний эпизод CppCast по проектированию для тестирования. В подкасте отмечается, что если вы обнаружите, что ваш код сложно протестировать, это означает, что он слишком тесно связан и, следовательно, плохо спроектирован.

Он также (справедливо, ИМХО) рекомендует тестировать только общедоступные функции. Если вам понадобится протестировать частные функции, вам, вероятно, следует провести рефакторинг кода.

Один из способов сделать это — разбить ваш код на несколько классов с общедоступными функциями, которые вы хотите протестировать. Затем ваш составной класс может либо создавать и владеть классом напрямую (подходит, если это базовый тип без зависимостей или собственных сложных ресурсов, таких как векторный или строковый класс), либо может использовать внедрение зависимостей для передачи зависимостей в качестве конструктора или параметров метода (подходит для баз данных, сетевых подключений, файловые системы и т.д.).

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

Это основной совет. В вашем конкретном случае похоже, что вы пытаетесь переопределить невиртуальную функцию в вашем TestService . Что именно вы хотите протестировать?

Я бы не ожидал EXPECT_TRUE(service.registerCallback(SessionCallback_t)); компиляции, потому SessionCallback_t что имена типа, а не экземпляр типа, поэтому вы не можете передать его. Опять же, чего вы пытаетесь достичь?


Обновление для комментариев:

Для издевательства требуются виртуальные функции (или утиный ввод) и внедрение зависимостей.

Если вы просто хотите протестировать registerCallback() , я подозреваю, что вам вообще не нужен макет. Скорее, вам нужно взглянуть на документацию функции, чтобы увидеть, что, по ее словам, она будет делать — иногда называемую контрактом. Например, каковы предварительные и постусловия функции? С какими случаями ошибок он может столкнуться? Это то, что должен охватывать модульный тест.

Например, сохраняет ли он только один обратный вызов (подсказка: как написано, да)? Что происходит, когда вы вызываете его, когда уже зарегистрирован обратный вызов? Допускает ли он передачу инициализированных по умолчанию std::function объектов?

Главный вопрос в том, как вы проверяете правильность вашего теста. Если вы начнете запускать уведомления при обратном вызове, вы выходите за рамки тестирования этой функции в изоляции. Вместо этого вы можете создать класс доступа в своем тесте, чтобы публиковать то, что является закрытым, чтобы вы могли проверять. Тем не менее, вы не можете сравнивать std::function на равенство, поэтому лучшее, что вы можете сделать, это вызвать его и проверить, произойдет ли ожидаемый побочный эффект:

 class TestService : public Service {
public:
    const SessionCallback_tamp; getCallback() const { return mCallback; }
};

struct TestCallback
{
    int mCount = 0;
    void operator()( const Sessionamp; ) {   mCount; }
};
  

Затем в вашем тесте вы можете написать такие тесты, как:

 TEST_F(MyTestService , Test_registerCallback_BadCallback) {
    auto service = TestService{};
    EXPECT_THROW( service.registerCallback( SessionCallback_t{} ), std::out_of_range );
}   

// Register and check that it's our callback
TEST_F(MyTestService , Test_registerCallback_CallbackSaved) {
    auto service  = TestService{};
    auto callback = TestCallback{};

    EXPECT_TRUE( service.registerCallback( callback ) );
    EXPECT_EQ( callback.mCount, 0 );

    auto actualCallback = service.getCallback();
    EXPECT_TRUE( actualCallback );

    actualCallback();
    EXPECT_EQ( callback.mCount, 1 );
}   

TEST_F(MyTestService , Test_registerCallback_CallbackOverwrite) {
    auto service   = TestService{};
    auto callback1 = TestCallback{};
    auto callback2 = TestCallback{};

    EXPECT_TRUE( service.registerCallback( callback1 ) );
    EXPECT_TRUE( service.registerCallback( callback2 ) );
    EXPECT_EQ( callback1.mCount, 0 );
    EXPECT_EQ( callback2.mCount, 0 );

    auto actualCallback = service.getCallback();
    EXPECT_TRUE( actualCallback );

    actualCallback();
    EXPECT_EQ( callback1.mCount, 0 );
    EXPECT_EQ( callback2.mCount, 1 );
}   
  

Комментарии:

1. Да, как я могу выполнить тест unity для невиртуальной функции, т.е. registerCallback() в моем примере. Пожалуйста, обратите внимание, что я не предполагаю вносить изменения в исходный код.

2. Обновлено, чтобы попытаться ответить на ваше уточнение.