Gmock - InSequence против RetiresOnSaturation

Я не понимаю следующий пример gmock:

{
    InSequence s;

    for (int i = 1; i <= n; i++) {
        EXPECT_CALL(turtle, GetX())
        .WillOnce(Return(10*i))
        .RetiresOnSaturation();
    }
}

Когда я удаляю .RetiresOnSaturation (), приведенный выше код работает так же - GetX возвращает 10, 20 и так далее. В чем причина использования .RetiresOnSaturation (), когда мы также используем объект В последовательности? Не могли бы вы это объяснить?

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

iheanyi 25.03.2020 23:52
Стоит ли изучать 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
1
6 509
2

Ответы 2

В данном конкретном примере RetiresOnSaturation() ничего не меняет. Когда последнее ожидание в последовательности насыщается, оно остается активным, но насыщенным. Дальнейший вызов приведет к сбою теста.

RetiresOnSaturation() обычно используется при наложении ожиданий. Например:

class Turtle {
  public:
    virtual int GetX() = 0;
};

class MockTurtle : public Turtle {
  public:
    MOCK_METHOD0(GetX, int());
};

TEST(GmockStackoverflow, QuestionA)
{
    MockTurtle turtle;

    // General expectation - Perhaps set on the fixture class?
    EXPECT_CALL(turtle, GetX()).WillOnce(Return(0));

    // Extra expectation
    EXPECT_CALL(turtle, GetX()).WillOnce(Return(10)).RetiresOnSaturation();

    turtle.GetX();
    turtle.GetX();
}

Это свойство можно использовать в сочетании с InSequence, когда последовательность ожидаемых событий перекрывает другое ожидание. В этом сценарии последнее ожидание в последовательности должно быть помечено как RetiresOnSaturation(). Обратите внимание, что нужно отмечать только последнее ожидание, потому что, когда ожидание в последовательности насыщается, оно отменяет предварительные ожидания.

Пример ниже демонстрирует, как это может работать на практике. Удаление RetiresOnSaturation() приводит к сбою теста.

TEST(GmockStackoverflow, QuestionB)
{
    MockTurtle turtle;

    EXPECT_CALL(turtle, GetX()).WillOnce(Return(0));

    {
        InSequence s;
        EXPECT_CALL(turtle, GetX()).WillOnce(Return(10));
        EXPECT_CALL(turtle, GetX()).WillOnce(Return(10)).RetiresOnSaturation();
    }

    turtle.GetX();
    turtle.GetX();
    turtle.GetX();
}

По моему опыту, некоторые (хорошо, возможно, многие) разработчики сталкиваются с проблемой, такой как сообщение об ошибке gtest, обнаруживают, что RetiresOnSaturation() устраняет проблему, и затем приобретают привычку щедро разбрызгивать RetiresOnSaturation() на протяжении своих модульных тестов - потому что это решает проблемы. . Это, по-видимому, проще, чем рассуждать о том, что должен выполнять тестовый пример. С другой стороны, мне нравится думать о том, что должно произойти (согласно контракту API задокументированный), в каком порядке - который может быть частичным, если вы используете After() или не все в той же последовательности - и что делает более выразительные конструкции вроде InSequence или After() естественно приходить мне в голову.

Итак, как заявил Адам Кейси, нет никаких технических причин, но ИМО может быть проблема магического мышления или недостаточной подготовки.

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

Приложение A: Пример, когда безвозмездный RetiresOnSaturation() ухудшает качество сообщений, и да, я видел такой код:

EXPECT_CALL(x, foo()).WillOnce(Return(42)).RetiresOnSaturation();

Если x.foo() вызывается более одного раза, скажем, дважды, то без RetiresOnSaturation() вы бы получили сообщение об ошибке типа «Нет соответствия ожиданиям для foo ()… Ожидается: вызывается один раз… Фактически: вызывается дважды (перенасыщено)», который как можно более конкретен. Но из-за RetiresOnSaturation() вы получите только предупреждение «Неожиданный вызов функции foo ()», которое сбивает с толку и бессмысленно.

Приложение B: В вашем примере также возможно, что рефакторинг для использования InSequence был произведен постфактум, и человек, выполняющий рефакторинг, не осознавал, что RetiresOnSaturation() теперь является избыточным. Вы можете сделать "виноватым" в своей системе контроля версий, чтобы проверить.

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