Как задать последовательность вызовов в 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. 

Для проверки, что метод не вызывается, можно сделать EXPECT_CALL(...).Times(0).
Похожие вопросы