Введение в Google Mock
Введение в Google Mock

1 Введение

1.1 Знакомство с Google Mock

Google Mock — это среда тестирования для C++, разработанная Google. Она является частью среды тестирования Google Test. gmock позволяет разработчикам создавать макеты объектов, которые могут заменить реальные зависимости в модульных тестах, делая тестирование более гибким и независимым. Используя gmock, разработчики могут сосредоточиться на тестировании корректности логики кода, не беспокоясь о сложности внешних зависимостей.

1.2Почему стоит выбрать Google Mock

Среди множества сред тестирования C++ gmock выделяется своими мощными функциями и простотой использования. Вот некоторые из основных причин выбрать gmock:

Гибкость: gmock поддерживает настраиваемое поведение моделирования и может моделировать сложные зависимости.

lПростота использования: дизайн API gmock прост и интуитивно понятен, его легко освоить и использовать.

lПоддержка сообщества. Будучи продуктом Google, gmock имеет активное сообщество и богатые ресурсы документации.

lИнтеграция: gmock можно легко интегрировать с Google Test, чтобы обеспечить универсальное решение для тестирования.

2 Основы Google Mock

2.1 Важность тестирования

Прежде чем мы углубимся в Google Mock, мы должны сначала осознать важность тестирования при разработке программного обеспечения. Тестирование — ключевая часть обеспечения качества программного обеспечения, помогающая нам находить и исправлять потенциальные ошибки и дефекты. Модульное тестирование — это самая базовая форма тестирования, которая позволяет нам независимо тестировать различные части кода.

2.2 Установка и настройка Google Mock

Прежде чем мы начнем использовать Google Mock, нам необходимо сначала установить и настроить его. Вот основные шаги по установке Google Mock:

1. Получите Google Test. Поскольку Google Mock является частью Google Test, нам необходимо сначала получить исходный код Google Test.

2. Скомпилируйте Google Test. Скомпилируйте библиотеку Google Test в соответствии с вашей операционной системой и компилятором.

3. Настройте проект. Настройте путь к заголовочному файлу и пути к библиотеке Google Test и Google Mock в своем проекте.

2.3 Структура тестовых случаев

Типичный тестовый пример обычно включает в себя следующие части:

lТестовая сборка: установите среду и условия, необходимые для тестирования.

lВыполнить тест: запустить тестируемый код.

l Утверждение: проверьте, соответствует ли вывод кода ожидаемому.

lОчистка: очистка окружающей среды после завершения теста.

Язык кода:javascript
копировать
#include <gtest/gtest.h>
class MyMathTest : public ::testing::Test {
protected:
    void SetUp() override {
        // тестовая сборка
    }
    void TearDown() override {
        // убирать
    }
};
TEST_F(MyMathTest, TestAddition) {    
    // Выполнять
    int result = 3 + 2;
    // утверждение
    EXPECT_EQ(5, result);
}

2.4 Шаги по написанию тестовых примеров

Написание тестовых случаев обычно следует следующим шагам:

1. Определите цель тестирования: уточните часть кода или функцию, которую вы хотите протестировать.

2. Напишите тестовый код. Используйте макросы и утверждения Google Test для написания тестовой логики.

3. Запустите тест. Скомпилируйте и запустите тест, чтобы проверить, соответствуют ли результаты ожидаемым.

4. Анализ и корректировка: корректируйте тестовые примеры или тестируемый код на основе результатов тестирования.

Язык кода:javascript
копировать
#include <gmock/gmock.h>
#include <gtest/gtest.h>
           
class NetworkService {
    virtual std::string fetchData(){return "";} 
};
           
class MockNetworkService : public NetworkService {
public:
    MOCK_METHOD(std::string, fetchData, (), (override));
};    
TEST(NetworkServiceTest, FetchDataTest) {
    MockNetworkService mockService;
    EXPECT_CALL(mockService, fetchData())
        .WillOnce(testing::Return("Mocked Data"));
    std::string result = mockService.fetchData();
    EXPECT_EQ("Mocked Data", result);
}

3 Создайте тестовые примеры

3.1 Важность тестовых случаев

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

3.2 Основные принципы тестовых случаев

При написании тест-кейсов следует следовать следующим основным принципам:

l Независимость: каждый тестовый пример должен выполняться независимо от других тестовых примеров и не должен зависеть от внешнего состояния.

l Повторяемость. Вы должны получать одни и те же результаты независимо от того, когда и где вы запускаете тестовые примеры.

lАвтоматизация: тестовые примеры должны выполняться автоматически, чтобы уменьшить количество ручного вмешательства.

l Всеобъемлющий охват. Тестовые примеры должны охватывать все важные функциональные точки и граничные условия.

3.3 Структура тестовых случаев

Полный тестовый пример обычно включает в себя следующие части:

lПредварительные условия: условия, которые необходимо выполнить перед началом теста.

lВходные данные: входные данные для тестового примера.

lШаги выполнения: конкретные шаги для выполнения теста.

lОжидаемые результаты: ожидаемые результаты после завершения теста.

lЛогика проверки: логика проверки соответствия фактических результатов ожидаемым.

3.4 Пример: простой тестовый пример сложения

Язык кода:javascript
копировать
#include <gtest/gtest.h>
class Adder {
public:
    int add(int a, int b) {
        return a + b;
    }
};
TEST(AdderTest, HandlesPositiveNumbers) {
    Adder adder;
    EXPECT_EQ(5, adder.add(2, 3));
}
TEST(AdderTest, HandlesNegativeNumbers) {
    Adder adder;
    EXPECT_EQ(-1, adder.add(-2, 1));
}
TEST(AdderTest, HandlesZero) {
    Adder adder;
    EXPECT_EQ(0, adder.add(0, 0));
}

3.5 Параметризация тестовых случаев

Язык кода:javascript
копировать
#include <gtest/gtest.h>
#include <tuple>
class Adder {
public:
    int add(int a, int b) {
        return a + b;
    }
};
class AdderTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};

TEST_P(AdderTest, AddReturnsExpectedResult) {
    Adder adder;
    int a, b, expected;
    std::tie(a, b, expected) = GetParam();
    EXPECT_EQ(expected, adder.add(a, b));
}
INSTANTIATE_TEST_CASE_P(AdderTests, AdderTest,
             ::testing::Values(std::make_tuple(2, 3, 5),
                            std::make_tuple(-2, 1, -1),
                            std::make_tuple(0, 0, 0)));

3.6 Организация тест-кейсов

В больших проектах очень важно правильно организовать тест-кейсы. Обычно тестовые примеры должны быть организованы в соответствии с тестируемым классом или модулем. Использование соглашений об именах и структур каталогов может помочь поддерживать и находить тестовые примеры.

3.7 Пример: тестирование сложной логики

Давайте рассмотрим более сложный пример, тестируя простой класс банковского счета:

Язык кода:javascript
копировать
#include <gtest/gtest.h>
class BankAccount {
public:
    BankAccount(int balance) : balance_(balance) {}
    void deposit(int amount) { balance_ += amount; }
    int getBalance() const { return balance_; }
private:
    int balance_;
};
TEST(BankAccountTest, DepositIncreasesBalance) {
    BankAccount account(100);
    account.deposit(50);
    EXPECT_EQ(150, account.getBalance());
}
TEST(BankAccountTest, DepositWithNegativeAmountThrows) {
    BankAccount account(100);
    account.deposit(-50);
    EXPECT_EQ(50, account.getBalance());    
}

4. Основные понятия Mocking

4.1 Что такое насмешка?

Mocking — это метод тестирования, который позволяет тестировщикам моделировать (издеваться) над поведением объекта или интерфейса, чтобы изолировать тестируемый код во время тестирования. Мок-объекты часто используются для замены реальных зависимостей, чтобы тесты могли выполняться независимо от внешних систем или компонентов.

4.2 Разница между Mocking и Stub

lMock: обычно используется для проверки правильности вызова зависимостей тестируемым кодом, включая количество вызовов, параметры, порядок вызова и т. д.

lStub: возвращает заранее определенные данные ответа, которые в основном используются для проверки логики кода, а не для проверки правильности вызова.

4.3 Зачем использовать Mocking?

lИзоляция: Mocking позволяет запускать тесты независимо от внешних систем, повышая стабильность и надежность тестов.

lГибкость: можно моделировать различные сложные ситуации, включая ошибки, исключения, задержки и т. д.

lЭффективность: позволяет избежать взаимодействия с внешними системами и ускоряет выполнение тестов.

4.4. Издевательство с помощью Google Mock

Google Mock предоставляет богатый набор API для создания и настройки объектов Mock. Вот основные шаги для создания макетов с помощью Google Mock:

1. Определите макетный интерфейс. Определите макетную версию в соответствии с классом или интерфейсом, который необходимо имитировать.

2. Используйте макрос MOCK_METHOD: определите метод Mock в интерфейсе Mock.

3. Установите ожидания: используйте EXPECT_CALL, чтобы установить ожидаемое поведение Mock-объекта.

4. Проверьте вызов. В конце теста Google Mock автоматически проверит, соответствует ли вызов объекта Mock ожиданиям.

4.5 Пример: имитация простой зависимости

Язык кода:javascript
копировать
#include <gtest/gtest.h>
class Database {  
public:  
    virtual ~Database() {}  
    virtual std::string query(const std::string& sql) const{return sql;}   
};  
class MockDatabase:public Database {
public:
    MOCK_METHOD(std::string, query, (const std::string& sql), (const, override));
};
class DataProcessor {
public:
    explicit DataProcessor(Database* db) : db_(db) {}
    std::string process(const std::string& data) {
        return data + " processed with " + db_->query("SELECT * FROM table");
}
private:
    Database* db_;
};    
           
TEST(DataProcessorTest, ProcessesDataWithDatabaseQuery) {
    MockDatabase mockDb;
    DataProcessor processor(&mockDb);
    EXPECT_CALL(mockDb, query("SELECT * FROM table"))
        .WillOnce(testing::Return("Mocked query result"));
    std::string result = processor.process("Data");
    EXPECT_EQ("Data processed with Mocked query result", result);
}

4.6 Продвинутые методы издевательств

lПоследовательные вызовы: используйте InSequence EXPECT_CALL для имитации последовательности вызовов методов.

l Любое количество вызовов. Используйте Times(), чтобы указать диапазон раз, когда метод может быть вызван.

lОбъединить Mock и Stub: используйте поведение Mock и Stub одновременно в одном Mock-объекте.

4.6.1 Использование Mock для проверки последовательности вызова метода

1) Ожидайте всех последовательных вызовов

Язык кода:javascript
копировать
#include <gtest/gtest.h>
using ::testing::_;
using ::testing::InSequence;
           
class MyClass {
public:
    virtual bool firstMethod(int){return true;}    
    virtual bool secondMethod(int){return true;}
};
           
class MockMyClass {
public:
    MOCK_METHOD(bool, firstMethod,(int));
    MOCK_METHOD(bool, secondMethod,(int));
};
TEST(SomeClassTest, CallsMethodsInOrder) {
    MockMyClass mock;
    {
        InSequence seq;
        EXPECT_CALL(mock, firstMethod(testing::_))
            .WillOnce(testing::Return(true));
        EXPECT_CALL(mock, secondMethod(testing::_))
            .WillOnce(testing::Return(true));
    }
           
    //EXPECT_EQ(true, mock.firstMethod(1));
    //EXPECT_EQ(true, mock.secondMethod(2));
    EXPECT_EQ(true, mock.secondMethod(2));
    EXPECT_EQ(true, mock.firstMethod(1));        
}

Если вы выполните приведенный выше код, будет сообщено об ошибке.

Язык кода:javascript
копировать
[  FAILED  ] SomeClassTest.CallsMethodsInOrder (1 ms)
[----------] 1 test from SomeClassTest (1 ms total)
           
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SomeClassTest.CallsMethodsInOrder

Будет правильно, если вы сначала выполните метод «mock.firstMethod(1)», а затем «mock. SecondMethod(2).

2) Ожидайте частичных последовательных вызовов

Предложение After EXPECT_CALL может выполнять частичные последовательные вызовы.

Используя предложение InSequence()

Язык кода:javascript
копировать
using ::testing::Sequence;
  ...
  Sequence s1, s2;
  EXPECT_CALL(foo, A()).InSequence(s1, s2);
  EXPECT_CALL(bar, B()).InSequence(s1);
  EXPECT_CALL(bar, C()).InSequence(s2);
  EXPECT_CALL(foo, D()).InSequence(s2);

Порядок выполнения

Как показано ниже (where s1 is A -> B, and s2 is A -> C -> D):

Язык кода:javascript
копировать
S1:A--------B
   |
S2:A--------C ---------D

4.6.2 Любое количество звонков

EXPECT_CALL(mock_object,method_name(matchers...)) создает фиктивный объект mock_object. Этот объект имеет метод с именем имя_метода, а параметры метода являются сопоставителями.... EXPECT_CALL необходимо использовать перед любыми фиктивными объектами. Следующие методы необходимо вызывать в следующем порядке:

Язык кода:javascript
копировать
EXPECT_CALL(mock_object, method_name(matchers...)) 
    .With(multi_argument_matcher)    // Can be used at most once
    .Times(cardinality)             // Can be used at most once
    .InSequence(sequences...)       // Can be used any number of times
    .After(expectations...)         // Can be used any number of times
    .WillOnce(action)               // Can be used any number of times
    .WillRepeatedly(action)         // Can be used at most once
    .RetiresOnSaturation();         // Can be used at most once

4.6.3 Объединение макета и заглушки

В одном и том же Mock-объекте мы можем использовать одновременно поведение Mock и Stub, что позволяет нам гибко контролировать поведение Mock-объекта в разных сценариях тестирования.

Язык кода:javascript
копировать
#include <gtest/gtest.h>  
    
using ::testing::_; 
using ::testing::Invoke;     
class Database {  
public:  
    virtual ~Database() {}  
    virtual std::string query(const std::string& sql) const{return sql;}
    virtual void connect(){};
};  
class MockDatabase:public Database {
public:
    MOCK_METHOD(std::string, query, (const std::string& sql), (const, override));
    MOCK_METHOD(void, connect, (), (override)); 
};
TEST(SomeClassTest, CombineMockAndStub) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, connect()) //куча
        .Times(1);
    ON_CALL(mockDb, query(_)) //mock
        .WillByDefault(testing::Return("Stubbed Query Result"));
           
    mockDb.connect(); 
    EXPECT_EQ("Stubbed Query Result",mockDb.query("query"));
}

4.7 Имитация сложных типов

Иногда нам нужно имитировать методы, возвращающие сложные типы. Google Mock предоставляет фабричные функции, такие как NiceMock и StrictMock, которые могут упростить создание и настройку объектов Mock.

4.7.1NiceMock

NiceMock — это оболочка, предоставляемая Google Mock (gmock), которая позволяет создавать более «щадящие» макеты объектов. В отличие от StrictMock, NiceMock не генерирует ошибку для неуказанных вызовов, но по умолчанию генерирует соответствующее возвращаемое значение или поведение.

Вот простой пример NiceMock:

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
#include <gtest/gtest.h>     
  
using ::testing::Return;  
  
// определить интерфейс  
class MyInterface {  
public:  
    virtual int Foo() = 0;  
    virtual void Bar(int x) = 0;  
}
// Создайте макет класса для интерфейса.  
class MockMyInterface : public MyInterface {  
public:      
    MOCK_METHOD(int, Foo, (), (override));  
    MOCK_METHOD(void, Bar, (int), (override));  
};  
TEST(NiceMockTest, NiceMockExample) {  
    // Использование NiceMock для переноса MockMyInterface  
    ::testing::NiceMock nice_mock;  
    
      // Указано желаемое поведение Foo(), но не указано желаемое поведение Bar().  
    EXPECT_CALL(nice_mock, Foo()).WillOnce(Return(42));  
      // Вызов Foo() должен вернуть 42  
    EXPECT_EQ(nice_mock.Foo(), 42);  
      // При вызове Bar() без указания желаемого поведения NiceMock не сообщит об ошибке и по умолчанию выполняет бездействующую операцию.  
    nice_mock.Bar(10);  
}

4.7.2 StrictMock

StrictMock — это еще одна оболочка, предоставляемая Google Mock (gmock). В отличие от NiceMock, StrictMock сообщит об ошибке для неуказанных вызовов. Это означает, что вы должны указать желаемое поведение для всех методов макетного объекта, иначе тест завершится неудачно, если во время теста будет вызван метод без установленных ожиданий. Вот пример использования StrictMock:

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
#include <gtest/gtest.h> 
    
using ::testing::Return;  
// определить интерфейс      
class MyInterface {  
public:  
    virtual int Foo() = 0;  
    virtual void Bar() = 0;  
}; 
// Создать для интерфейса mock добрый  
class MockMyInterface : public MyInterface {  
public:  
    MOCK_METHOD(int, Foo, (), (override));  
    MOCK_METHOD(void, Bar, (), (override));  
};   
TEST(StrictMockTest, StrictMockExample) {  
    // использовать StrictMock Упаковка MockMyInterface  
    ::testing::StrictMock strict_mock;  
    
  
    // для Foo() и Bar() Метод указывает ожидаемую строку для  
    EXPECT_CALL(strict_mock, Foo()).WillOnce(Return(42));  
    EXPECT_CALL(strict_mock, Bar());  
  
    // вызов Foo(), должен вернуть 42  
    EXPECT_EQ(strict_mock.Foo(), 42);  
      
    // вызов Bar(), оправдывая установленные ранее ожидания  
    strict_mock.Bar();  
  
    // если попытатьсявызов Никаких ожиданий не установленоmockметод,тест провалится  
    // Например: strict_mock.SomeUndeclaredMethod(); // Это приведет к сбою теста  
}

l Используя метод NiceMock, если вы попытаетесь вызвать фиктивный метод без установки ожиданий, тест завершится неудачно;

lПри использовании метода StrictMock, если вы попытаетесь вызвать фиктивный метод без установки ожиданий, тест завершится неудачей.

5. Продвинутые навыки насмешки

5.1 Введение

Продвинутые навыки макетирования — это продвинутые навыки модульного тестирования, которые могут помочь тестировщикам более точно моделировать сложные сценарии, тем самым улучшая охват и качество тестирования. В этом разделе мы углубимся в некоторые продвинутые методы создания макетов и продемонстрируем их применение на богатых примерах.

5.2. Используйте ON_CALL для настройки Mock-поведения.

Макрос ON_CALL позволяет нам указать поведение по умолчанию для методов объекта Mock, что очень полезно при тестировании, особенно когда методы объекта Mock необходимо вызывать повторно в разных тестовых случаях.

знак:

Язык кода:javascript
копировать
ON_CALL(mock_object,method_name(matchers...))

Определяет действие, выполняемое после вызова метода имя_метода объекта макет_объекта. Если сопоставители опущены, это будет похоже на то, что каждый параметр имеет значение «_», что означает, что может быть сопоставлено любое значение.

Язык кода:javascript
копировать
ON_CALL(mock_object, method_name(matchers...))
    .With(multi_argument_matcher)  // Can be used at most once    
    .WillByDefault(action);        // Required

lWith(multi_argument_matcher):Сделайте параметрыдляобщее совпадение。GoogleTestСделать все параметрыдляодинtupleДаватьmatcher,multi_argument_matcherв конце концов будетдаMatcher<std::tuple<A1, …, An>>。этотвызовмаксимум один раз

lWillByDefault(action): указывает действие, которое будет выполнено после вызова фиктивной функции. Этот вызов происходит ровно один раз.

посмотрите пример

Язык кода:javascript
копировать
#include <gmock/gmock.h>  

class Database {  
public:  
    virtual ~Database() {}  
    virtual std::string query(const std::string& sql) const{return sql;}   
}; 
           
class MockDatabase:public Database {
public:
    MOCK_METHOD(std::string, query, (const std::string& sql), (const, override));
};
class DataProcessor {
public:
    explicit DataProcessor(Database* db) : db_(db) {}    
    std::string process(const std::string& data) {
        return data + " processed with " + db_->query("SELECT * FROM table");
}
private:
    Database* db_;
}; 
           
TEST(AdvancedMockingTest, CustomDefaultBehavior) {
    MockDatabase mockDb;
    ON_CALL(mockDb, query(testing::_))
        .WillByDefault(testing::Return("Default Query Result"));
           
    EXPECT_EQ("Default Query Result", mockDb.query("Any SQL"));
}

5.3 Моделирование исключений

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

Язык кода:javascript
копировать
class Calculator {  
public:  
     virtual int divide(int a, int b) = 0;    
};    
class MockCalculator : public Calculator {      
public:  
    MOCK_METHOD(int, divide, (int a, int b), (override));  
}; 
TEST(CalculatorTest, TestCalculator) {  
    MockCalculator mockCalculator;      
    // Установите строку макета объекта для  
    //… 
    EXPECT_CALL(mockCalculator, divide(testing::_,0))
        .WillOnce(testing::Throw(std::runtime_error("Divisor cannot be 0")));   
    // Проверьте строку макета объектадля  //…
    EXPECT_THROW(mockCalculator.divide(6, 0), std::runtime_error); 
}

Имитируйте исключения посредством тестирования::Throw и EXPECT_THROW.

5.4 Использование функции обратного вызова Invoke

Функция Invoke позволяет нам вызывать функцию обратного вызова в методе Mock, что очень полезно, когда нам нужно динамически возвращать результаты на основе входных параметров.

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
class Database {  
public:  
    virtual ~Database() {}  
    virtual std::string query(const std::string& sql) const{return sql;}   
};      
class MockDatabase:public Database {
public:
    MOCK_METHOD(std::string, query, (const std::string& sql), (const, override));
};
class DataProcessor {
public:
    explicit DataProcessor(Database* db) : db_(db) {}
    std::string process(const std::string& data) {
        return data + " processed with " + db_->query("SELECT * FROM table");
    }
private:
    Database* db_;
};
TEST(AdvancedMockingTest, UseInvokeForDynamicReturn) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, query(testing::_))
        .WillOnce(testing::Invoke([](const std::string& sql) {
            return "Result for " + sql;
        }));
    EXPECT_EQ("Result for SELECT * FROM table", mockDb.query("SELECT * FROM table"));    
}

5.5 Моделирование сложных структур данных

Когда метод Mock возвращает сложную структуру данных, мы можем использовать WithArgs для сопоставления определенных параметров и возврата соответствующих результатов.

Язык кода:javascript
копировать
// Определить структуру  
struct QueryResult {  
    std::string name;  
    int age;  
};  
  
// Гипотетический функциональный интерфейс  
class DatabaseService {  
public:  
    virtual QueryResult executeQuery(const std::string& sql) = 0;  
};  
// Mockдобрый  
class MockDatabaseService : public DatabaseService {  
public:  
    MOCK_METHOD(QueryResult, executeQuery, (const std::string&), (override));  
};      
TEST(MockDatabaseService, MockComplexReturnType) {
    MockDatabaseService mockDb;
    QueryResult alice{"Alice", 30};
    EXPECT_CALL(mockDb, executeQuery(testing::_))
        .Times(2)
        .WillOnce(testing::WithArgs<0>(testing::Invoke([](const std::string& sql) {
            return QueryResult{"Alice", 30};
        })))
        .WillRepeatedly(testing::WithArgs<0>(testing::Invoke([](const std::string& sql) {
            return QueryResult{"Bob", 40};
        })));
    QueryResult result = mockDb.executeQuery("Alice");
    EXPECT_EQ("Alice",result.name);
    EXPECT_EQ(30,result.age);
    result = mockDb.executeQuery("Bob");
    EXPECT_EQ("Bob",result.name);
    EXPECT_EQ(40,result.age);
}

5.6 Моделирование побочных эффектов вызовов методов

Иногда нам может потребоваться смоделировать побочные эффекты вызова метода, такие как изменение общего состояния или запуск обратного вызова.

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
using ::testing::_; 
using ::testing::Invoke;
class Database {  
public:  
    virtual ~Database() {}  
    virtual std::string update(int id, const std::string& value) const{return value;}   
};  
class MockDatabase:public Database {
public:
    MOCK_METHOD(std::string, update, (int id, const std::string& value), (const, override));
};   
TEST(MockDatabase, SimulateSideEffects) {
    MockDatabase mockDb;
    std::string sideEffect;
    EXPECT_CALL(mockDb, update(_, _))
        .WillOnce(Invoke([&sideEffect](int id, const std::string& value) {
            return sideEffect = "Updated " + std::to_string(id) + " to " + value;  
        }));
               
    mockDb.update(1, "value");
    EXPECT_EQ("Updated 1 to value", sideEffect);
}

5.7 Множественные вызовы моделируемых методов

Время можно использовать для указания количества вызовов метода Mock, что очень полезно для тестирования циклов или рекурсивной логики.

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
           
class NetworkService {
    virtual std::string fetchData(){return "";} 
};
class MockNetworkService : public NetworkService {
public:
    MOCK_METHOD(std::string, fetchData, (), (override));
};
           
class MockDatabase:public NetworkService {
public:
    MOCK_METHOD(std::string, fetchData, (), (override));
};  
TEST(AdvancedMockingTest, MultipleInvocations) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, fetchData())    
        .Times(3)
        .WillRepeatedly(testing::Return("Data"));
           
    for (int i = 0; i < 3; ++i) {
        EXPECT_EQ("Data", mockDb.fetchData());
    }
}

5.8. Использование UnorderedElementsAre для сопоставления контейнеров

Когда параметром метода Mock является контейнер, мы можем использовать UnorderedElementsAre для сопоставления элементов в контейнере без указания порядка элементов.

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
class Database {  
public:  
    virtual ~Database() {}  
    virtual void insert(const std::vector& values){}
};  
class MockDatabase{
public:
    MOCK_METHOD(void, insert, (const std::vector& values));
}; 
// использоватьMockDatabaseдобрыйизметод      
void SomeFunctionUsingMockDatabase(MockDatabase& db) {  
    std::vector values = {3, 1, 2};  
    
    db.insert(values);  
}
//тестовый код  
TEST(AdvancedMockingTest, MatchUnorderedContainer) {  
    MockDatabase mockDb;  
    EXPECT_CALL(mockDb, insert(testing::UnorderedElementsAre(1, 2, 3)));  
    SomeFunctionUsingMockDatabase(mockDb);  
}

6. Утверждения и ожидания Google Mock

6.1 Важность утверждений

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

6.2 Базовые утверждения Google Test

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

lASSERT_TRUE:если условиедля Фальшивый,Тогда тест не пройден.

lEXPECT_TRUE:То же, что и выше,Но условиядля Фальшивыйчастест Продолжить выполнение。

lASSERT_EQ:Проверьте два значениядаравны или нет,если не равно Тогда тест не пройден.

Язык кода:javascript
копировать
TEST(BasicAssertionsTest, ChecksEquality) {
    int result = someFunction();
    ASSERT_EQ(42, result) << "someFunction did not return the expected value.";}

6.3 Ожидания

Ожидаемые вызовы — это механизм Google Mock, позволяющий указать, как объект Mock должен вызываться в тесте.

lEXPECT_CALL:создаватьодиножидатьвызов。

lTimes:Укажите ожиданиявызовраз。

Язык кода:javascript
копировать
TEST(MockExpectationsTest, CallsFunctionOnce) {
    MockFunction mock;
    EXPECT_CALL(mock, Call())
        .Times(1);
    mock.Call();
}

6.4 Сопоставители

Google Mock предоставляет богатые средства сопоставления, которые позволяют нам использовать сложные условия в ожидаемых вызовах.

ltesting::_:соответствовать любому значению。

ltesting::Eq(x):совпадение равноxценить.

ltesting::Le(x):Совпадает менее чемxценить.

ltesting::Ge(x):Соответствие больше или равно x ценить.

Язык кода:javascript
копировать
TEST(MockMatchersTest, MatchesSpecificValue) {
    MockFunction mock;
    EXPECT_CALL(mock, Call(testing::Ge(10)))
        .Times(1);
    mock.Call(10);  // Матч успешен    
}

6.5 Объединение утверждений

Объединение утверждений позволяет нам построить более сложную логику проверки.

ltesting::AllOf:все условия соблюдены。

ltesting::AnyOf:Любое условие соблюдено。

Язык кода:javascript
копировать
TEST(CombinationAssertionsTest, ChecksMultipleConditions) {
    int result = someFunction();
    ASSERT_THAT(result, testing::AllOf(testing::Ge(40), testing::Le(50)));
}

6.6 Действия по утверждению (Действия)

Действия утверждения используются в Google Mock, чтобы указать, что должен делать объект Mock при ожидаемом вызове.

ltesting::Return(x):возвращаемое значение x。

ltesting::Throw:выдать исключение。

Язык кода:javascript
копировать
TEST(MockActionsTest, ReturnsSpecificValue) {
    MockFunction mock;
    EXPECT_CALL(mock, Call())
        .WillOnce(testing::Return(42));
    EXPECT_EQ(42, mock.Call());
}

6.7 Валидаторы

Валидаторы позволяют нам выполнять пользовательскую логику проверки при вызове Mock-объекта.

ltesting::SaveArg:держатьвызовпараметр。

ltesting::InvokeWithoutArgs:вызовфункция без параметров。

Язык кода:javascript
копировать
TEST(MockValidatorsTest, SavesArgument) {
    MockFunction mock;
    int savedArg;
    EXPECT_CALL(mock, Call(testing::_))
        .WillOnce(testing::SaveArg<0>(&savedArg));
    mock.Call(42);
    ASSERT_EQ(42, savedArg);
}

6.8 Расширенное использование ожиданий и утверждений

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

InSequence:убеждатьсявызовпроисходить в определенном порядке。

testing::Each:соответствовать каждому элементу。

Язык кода:javascript
копировать
using ::testing::InSequence;
using ::testing::Return;
           
TEST(SequenceExpectationsTest, CallsInOrder) {
    InSequence seq;
    MockFunction mock;
    EXPECT_CALL(mock, Call(1))
        .WillOnce(Return(2));
    EXPECT_CALL(mock, Call(2))    
        .WillOnce(Return(3));
    EXPECT_EQ(2, mock.Call(1));
    EXPECT_EQ(3, mock.Call(2));
}

7 Интеграция

Судя по приведенным выше случаям. Две программы, калькулятор и доступ к данным, могут быть интегрированы.

7.1 Калькулятор

Calculator.cpp

Язык кода:javascript
копировать
#include <gmock/gmock.h> 
#include <gtest/gtest.h> 

  
class Calculator {  
public:  
    virtual ~Calculator() {}  
    virtual int add(int a, int b) = 0;  
    virtual int subtract(int a, int b) = 0;  
    virtual int multiply(int a, int b) = 0;  
    virtual int divide(int a, int b) = 0;    
};  
  
class MockCalculator : public Calculator {  
public:      
    MOCK_METHOD(int, add, (int a, int b), (override));  
    MOCK_METHOD(int, subtract, (int a, int b), (override));  
    MOCK_METHOD(int, multiply, (int a, int b), (override));  
    MOCK_METHOD(int, divide, (int a, int b), (override));  
};  
  
TEST(CalculatorTest, TestCalculator) {  
    MockCalculator mockCalculator;  
      
    // Установите строку макета объекта для  
    ON_CALL(mockCalculator, add(testing::_,testing::_))  
        .WillByDefault(testing::Invoke([](int a, int b) { return a + b; }));  
    ON_CALL(mockCalculator, subtract(testing::_,testing::_))  
        .WillByDefault(testing::Invoke([](int a, int b) { return a - b; })); 
    ON_CALL(mockCalculator, multiply(testing::_,testing::_))  
        .WillByDefault(testing::Invoke([](int a, int b) { return a * b; }));  
    ON_CALL(mockCalculator, divide(testing::_,testing::_))  
        .WillByDefault(testing::Invoke([](int a, int b) { return a / b; }));  
      
    // Проверьте строку макета объектадля  
    EXPECT_EQ(mockCalculator.add(2, 3), 5);  
    EXPECT_EQ(mockCalculator.subtract(5, 3), 2);    
    EXPECT_EQ(mockCalculator.multiply(5, 3), 15);
    EXPECT_EQ(mockCalculator.divide(6, 3), 2);  
}
           
TEST(CalculatorTest, TestCalculatorDivisorZero) {  
    MockCalculator mockCalculator;      
    // Установите строку макета объекта для  
    EXPECT_CALL(mockCalculator, divide(testing::_,0))
        .WillOnce(testing::Throw(std::runtime_error("Divisor cannot be 0")));   
// Проверьте строку макета объектадля
//…
    EXPECT_THROW(mockCalculator.divide(6, 0), std::runtime_error); 
}

7.2 Доступ к базе данных

Database.cpp

Язык кода:javascript
копировать
#include <gmock/gmock.h>  
  
#include <string>  
#include <unordered_map>  
#include <memory> 
#include <iostream>
#include <vector>
 
           
using ::testing::_; 
using ::testing::Invoke;
using ::testing::Return;
using ::testing::UnorderedElementsAre;
using ::testing::WithArgs;
using ::std::string;
using ::std::vector;
using ::std::to_string;
           
// Определить структуру  
struct QueryResult {  
    string name;  
    int age;  
};  
           
class Database {  
public:  
    virtual ~Database() {}  
    virtual string query(const string& sql) const{return sql;}
    virtual string fetchData(){return "";} 
    virtual string update(int id, const string& value) const{return value;}     
    virtual QueryResult executeQuery(const string& sql) = 0; 
    virtual void insert(const vector& values){}
    virtual void connect(){}
};  
           
           
class MockDatabase:public Database {
public:
    MOCK_METHOD(string, query, (const string& sql), (const, override));
    MOCK_METHOD(string, fetchData, (), (override));
    MOCK_METHOD(string, update, (int id, const string& value), (const, override));
    MOCK_METHOD(QueryResult, executeQuery, (const string&), (override));  
    MOCK_METHOD(void, insert, (const vector& values), (override));
    MOCK_METHOD(void, connect, (), (override)); 
};
           
class DataProcessor {
public:
    explicit DataProcessor(Database* db) : db_(db) {}
    string process(const string& data) {
        return data + " processed with " + db_->query("SELECT * FROM table");    
    }
           
private:
    Database* db_;
};
           
TEST(DataBaseTest, ProcessesDataWithDatabaseQuery) {
    MockDatabase mockDb;
    DataProcessor processor(&mockDb);
           
    EXPECT_CALL(mockDb, query("SELECT * FROM table"))
        .WillOnce(Return("Mocked query result"));
    EXPECT_EQ("Data processed with Mocked query result", processor.process("Data"));
}
           
TEST(DataBaseTest, CombineMockAndStub) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, connect())
        .Times(1);
    ON_CALL(mockDb, query(_))    
        .WillByDefault(Return("Stubbed Query Result"));
           
    mockDb.connect();
    EXPECT_EQ("Stubbed Query Result",mockDb.query("query"));
}
           
TEST(DataBaseTest, UseInvokeForDynamicReturn) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, query(_))
        .WillOnce(Invoke([](const string& sql) {
            return "Result for " + sql;
        }));
    EXPECT_EQ("Result for SELECT * FROM table", mockDb.query("SELECT * FROM table"));
}
           
TEST(DataBaseTest, CustomDefaultBehavior) {
    MockDatabase mockDb;
    ON_CALL(mockDb, query(_))
        .WillByDefault(Return("Stubbed Query Result"));
           
    EXPECT_EQ("Stubbed Query Result", mockDb.query("query"));    
}
           
TEST(DataBaseTest, FetchDataTest) {
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, fetchData())
        .WillOnce(Return("Mocked Data"));
    string result = mockDb.fetchData();
    EXPECT_EQ("Mocked Data", result);
}
           
TEST(DataBaseTest, SimulateSideEffects) {
    MockDatabase mockDb;
    string sideEffect;
    EXPECT_CALL(mockDb, update(_, _))
        .WillOnce(Invoke([&sideEffect](int id, const string& value) {
            return sideEffect = "Updated " + to_string(id) + " to " + value;  
        }));
    mockDb.update(1, "value");
    EXPECT_EQ("Updated 1 to value", sideEffect);
}
           
TEST(DataBaseTest, MultipleInvocations) {    
    MockDatabase mockDb;
    EXPECT_CALL(mockDb, fetchData())
        .Times(3)
        .WillRepeatedly(Return("Data"));
           
    for (int i = 0; i < 3; ++i) {
        EXPECT_EQ("Data", mockDb.fetchData());
    }
}
  
void SomeFunctionUsingMockDatabase(MockDatabase& db) {  
    vector values = {3, 1, 2};  
    
    db.insert(values);  
}  
  
TEST(DataBaseTest, MatchUnorderedContainer) {  
    MockDatabase mockDb;  
    EXPECT_CALL(mockDb, insert(UnorderedElementsAre(1, 2, 3)));  
    SomeFunctionUsingMockDatabase(mockDb);  
}
           
TEST(DataBaseTest, MockComplexReturnType) {    
    MockDatabase mockDb; 
           
    EXPECT_CALL(mockDb, executeQuery(_))
        .Times(2)
        .WillOnce(WithArgs<0>(Invoke([](const string& sql) {
            return QueryResult{"Alice", 30};
        })))
        .WillRepeatedly(WithArgs<0>(Invoke([](const string& sql) {
            return QueryResult{"Bob", 40};
        })));
    QueryResult result = mockDb.executeQuery("Alice");
    EXPECT_EQ("Alice",result.name);
    EXPECT_EQ(30,result.age);
    result = mockDb.executeQuery("Bob");
    EXPECT_EQ("Bob",result.name);
    EXPECT_EQ(40,result.age);
}
boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода