Как задать последовательность вызовов в Google Mock?

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

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

Вот пример кода: 
```cpp
MockVM mockVm;
Sequence csr;

EXPECT_CALL(mockVm, get_control(csr_id, _))
                    .InSequence(csr)
                    .WillOnce(SetArgReferee<1>(csr_val));
EXPECT_CALL(mockVm, get_register(src))
                .InSequence(csr)
                .WillOnce(Return(src_val));

EXPECT_CALL(mockVm, set_control(csr_id, expected_csr_value))
                .InSequence(csr);
impl->exec(&mockVm, &code);
```
Здесь методы <b>get_control</b> и <b>get_register</b> могут быть вызваны в любом порядке. Как можно это реализовать с помощью gMock?
  • 1 апреля 2025 г. 1:25
Ответы на вопрос 2
Чтобы реализовать последовательность вызовов в Google Mock, где метод `set_control` должен вызываться только после вызовов `get_control` и `get_register`, в то время как сами методы `get_control` и `get_register` могут вызываться в произвольном порядке, вы можете использовать `InSequence` только для метода `set_control`. При этом для методов `get_control` и `get_register` последовательность вызовов не будет обязательной.

Вот пример кода, который решает вашу задачу:

```cpp
#include <gmock/gmock.h>
#include <gtest/gtest.h>

using ::testing::_;
using ::testing::Return;
using ::testing::SetArgReferee;

class MockVM {
public:
    MOCK_METHOD(void, set_control, (int, int), ());
    MOCK_METHOD(void, get_control, (int, int&), ());
    MOCK_METHOD(int, get_register, (int), ());
};

class MyClass {
public:
    void exec(MockVM* vm, int* code) {
        int control_value;
        int reg_value = vm->get_register(1);
        vm->get_control(2, control_value);
        int result = reg_value + control_value; // example computation
        vm->set_control(2, result);
    }
};

TEST(MyClassTest, TestCallbackOrder) {
    MockVM mockVm;
    MyClass impl;

    int csr_id = 2;
    int src = 1;
    int csr_val = 10;
    int src_val = 5;
    int expected_csr_value = csr_val + src_val;

    // Create a sequence for set_control
    ::testing::Sequence seq;

    EXPECT_CALL(mockVm, get_control(csr_id, _))
        .WillOnce(SetArgReferee<1>(csr_val));

    EXPECT_CALL(mockVm, get_register(src))
        .WillOnce(Return(src_val));

    // Ensure that set_control is called after the other calls
    EXPECT_CALL(mockVm, set_control(csr_id, expected_csr_value))
        .InSequence(seq);

    // Call the method under test
    int code; // Example usage, may need to change based on your implementation
    impl.exec(&mockVm, &code);
}
```

В этом примере:
- Мы определили порядок вызовов так, чтобы `set_control` должен вызываться в последовательности (используя `InSequence`), но методы `get_control` и `get_register` могут вызываться в любом порядке.
- Мы назначаем ожидания для `get_control` и `get_register` без установки их в последовательность. Это приводит к тому, что они могут быть вызваны в любом порядке перед вызовом `set_control`. 

Таким образом, вы обеспечиваете, что `set_control` вызывается только после выполнения обоих предыдущих методов, что соответствует требованиям вашего теста.
Для проверки последовательности вызовов вам нужен метод (или часовой) InSequence. <br/> <br/> Для проверки, что метод не вызывается, можно сделать EXPECT_CALL(...).Times(0).
Похожие вопросы