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

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

bool Init(Uint8* dataPointer); 
bool Store(); //Writes the data from dataPointer into the NVRAM
bool Restore(); //Writes the data from NVRAM into the dataPointer

Мой тестовый сценарий следующий:

  1. Создать ClassUnderTest, также вызывает метод Init

  2. Вызов другого метода в моем ClassUnderTest, который вызывает Restore-метод. И я хочу иметь возможность управлять своим макетом таким образом, чтобы после вызова метода Restore было установлено значение dataPointer.

Или в псевдокоде:

MockFoo foo;
EXPECT_CALL(foo, Init(dataPointer)).WillOnce(Return(true));
EXPECT_CALL(foo, Restore()).WillOnce(DoAll(memcpy(dataPointer, testValues, sizeOf(testValues)), Return(true)));

Что я пробовал до сих пор:

  • Действия по умолчанию из googleMock (например, SaveArg): позволяет мне записывать данные в dataPointer, но только Init-вызов.
  • Написание ACTION_TEMPLATE для сохранения dataPointer в локальной переменной и последующего изменения его значения при вызове Restore: Насколько я понимаю, я могу добавить только VALUE_PARAMS в ACTION_TEMPLATE, поэтому я могу передать значения в шаблон, но не могу передать их снова по некоторому указателю.
  • Я просмотрел ActionInterface и полиморфные действия, если я правильно понимаю документацию, они имеют те же ограничения, что и ACTION_TEMPLATE, в отношении моей проблемы.

В итоге главный вопрос для меня: Есть ли способ сохранить dataPointer во время Init-звонка, чтобы использовать его позже?

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

Ответы 1

Ответ принят как подходящий

Лично я почти никогда не использую эти SaveArg, ACTION или другие мелкие функции gmock. Я предпочитаю использовать Invoke и просто определять свою собственную логику, которая должна вызываться всякий раз, когда выполняется вызов фиктивного метода. Это может показаться излишеством, но на самом деле очень часто оно читабельнее и короче:

class API {
public:
    virtual bool Init(uint8_t* dataPointer) = 0;
    virtual bool Store() = 0;
    virtual bool Restore() = 0;
};

class MockAPI : public API {
 public:
  MOCK_METHOD1(Init,
      bool(uint8_t* dataPointer));
  MOCK_METHOD0(Store,
      bool());
  MOCK_METHOD0(Restore,
      bool());
};

class ClassUnderTest {
public:
    explicit ClassUnderTest(std::shared_ptr<API> api): api_(api) {
        dataPtr_ = new uint8_t;
        api_->Init(dataPtr_);
    }
    ~ClassUnderTest() {
        delete dataPtr_;
    }
    bool anotherMethod() {
        api_->Restore();
        return true;
    }
    uint8_t takeALookAtTheData() {
        return *dataPtr_;
    }
private:
    std::shared_ptr<API> api_;
    uint8_t* dataPtr_;
};

using testing::_;
using testing::Invoke;

TEST(xxx, yyy) {
    auto mockApi = std::make_shared<MockAPI>();
    uint8_t* dataPtr(nullptr);
    uint8_t testValue = 123;
    ON_CALL(*mockApi, Init(_)).WillByDefault(Invoke([&dataPtr](uint8_t* dataPointer) {
        dataPtr = dataPointer;
        return true;
    }));
    ON_CALL(*mockApi, Restore()).WillByDefault(Invoke([&dataPtr, testValue]() {
        *dataPtr = testValue;
        return true;
    }));
    ClassUnderTest sut(mockApi);
    ASSERT_NE(nullptr, dataPtr);
    sut.anotherMethod();
    ASSERT_EQ(testValue, *dataPtr);
    ASSERT_EQ(testValue, sut.takeALookAtTheData());
}

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

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