Gmock: последовательные вызовы внутри блока с произвольным порядком блоков.C++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Gmock: последовательные вызовы внутри блока с произвольным порядком блоков.

Сообщение Anonymous »

Мой продуктивный код
Рассмотрим этот интерфейс:

Код: Выделить всё

class Interface
{
public:
virtual ~Interface = default;

virtual void open(int id) = 0;
virtual void close(int id) = 0;

protected:
// Default other special members here
};
Обратите внимание, что close() можно вызывать только после open(), и для обоих должен использоваться один и тот же идентификатор. Кроме того, close() должен быть вызван перед следующим вызовом open(). Идентификаторы не должны повторяться.
Теперь у меня есть несколько жестко закодированных идентификаторов, и мне нужно вызывать open() и close() для каждого идентификатора, отвечающего вышеупомянутым требованиям. . Правильная реализация:

Код: Выделить всё

void actCorrect(Interface & interface)
{
interface.open(1);
interface.close(1);
interface.open(2);
interface.close(2);
interface.open(3);
interface.close(3);
}
Порядок идентификаторов не важен, поэтому следующий код также будет приемлемым:

Код: Выделить всё

void actCorrect2(Interface & interface)
{
interface.open(2);
interface.close(2);
interface.open(1);
interface.close(1);
interface.open(3);
interface.close(3);
}
Однако следующий код должен завершиться ошибкой, поскольку вызов close(1) не упорядочен между open(1) и любым последующим open(_):

Код: Выделить всё

void actWrong(Interface & interface)
{
interface.open(1);
interface.open(2);
interface.open(3);
interface.close(1);
interface.close(2);
interface.close(3);
}
Примечание: это не очень хорошая реализация, но дело не в этом. Я выбрал его, чтобы подчеркнуть свою точку зрения, чтобы иметь возможность намеренно добавлять ошибку практически без изменений в коде.
Требуются надежные тесты
Я теперь хочу написать тесты с помощью GTest/GMock таким образом, чтобы тесты были устойчивы к рефакторингу с изменением порядка. Если быть точным, тесты должны:
  • все равно пройти успешно, если я изменю порядок open()/ пары. (см. actCorrect() и actCorrect2())
  • break, если open()/

    Код: Выделить всё

    close()Пары 
    разрываются так, что они больше не являются последующими вызовами экземпляра интерфейса. (см. actWrong())
Я пропускаю определение макета (оно должно быть очевидным) и предполагаю использование пространства имен ::testing ; для следующих фрагментов кода.
Чтобы выполнить требование 2, я мог бы использовать объект InSequence следующим образом:

Код: Выделить всё

MockInterface mock;
InSequence const seq;
EXPECT_CALL(mock, open(1));
EXPECT_CALL(mock, close(1));
EXPECT_CALL(mock, open(2));
EXPECT_CALL(mock, close(2));
EXPECT_CALL(mock, open(3));
EXPECT_CALL(mock, close(3));
act(mock);
Но это не соответствует требованию 1, actCorrect2() нарушит этот тест.
Вместо этого я мог бы использовать объекты Sequence, например:< /p>

Код: Выделить всё

MockInterface mock;
Sequence const seq1, const seq2, const seq3;
EXPECT_CALL(mock, open(1)).InSequence(seq1);
EXPECT_CALL(mock, close(1)).InSequence(seq1);
EXPECT_CALL(mock, open(2)).InSequence(seq2);
EXPECT_CALL(mock, close(2)).InSequence(seq2);
EXPECT_CALL(mock, open(3)).InSequence(seq3);
EXPECT_CALL(mock, close(3)).InSequence(seq3);
act(mock);
Этот метод будет принимать actCorrect() и actCorrect2(), таким образом выполняя требование 1, но он также будет принимать actWrong(). Это потому, что я не сказал GMock, что каждая последовательность должна быть завершена до начала следующей. (потому что я не знаю как)
Лучшее, что я могу придумать, — это ввести состояние для отслеживания текущего идентификатора и выдать ошибку, если он не совпадает:

Код: Выделить всё

MockInterface mock;
std::optional currentId;
EXPECT_CALL(mock, open(_)).WillRepeatedly(Invoke([&](int const id)
{
if (currentId.has_value())
{
throw std::runtime_error{ "Calling open() but is not closed" };
}
}));
EXPECT_CALL(mock, close(_)).WillRepeatedly(Invoke([&](int const id)
{
if (!currentId.has_value())
{
throw std::runtime_error{ "Calling close() but is not opened" };
}
if (*currentId != id)
{
throw std::runtime_error{ "Calling close() but was opened with a different id" };
}
}));
Но на самом деле между open() есть еще несколько вызовов/-пары, и мне пришлось бы настроить одинаковое действие для всех из них, что также похоже на частичную повторную реализацию логики. Если бы я хотел избежать дублирования идентификаторов, мне нужно было бы отслеживать и те, которые еще больше усложняют ситуацию.
Есть идеи, как добиться этого в рамках GMock? В идеале с помощью одной из операций секвенирования, например .InSequence() или .After().

Подробнее здесь: https://stackoverflow.com/questions/786 ... lock-order
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C++»