Рассмотрим этот интерфейс:
Код: Выделить всё
class Interface
{
public:
virtual ~Interface = default;
virtual void open(int id) = 0;
virtual void close() = 0;
protected:
// Default other special members here
};
Теперь у меня есть несколько жестко закодированных идентификаторов, и мне нужно вызывать open() и close() для каждого идентификатора, отвечающего вышеупомянутым требованиям. . Правильная реализация:
Код: Выделить всё
void actCorrect(Interface & interface)
{
interface.open(1);
interface.close();
interface.open(2);
interface.close();
interface.open(3);
interface.close();
}
Код: Выделить всё
void actCorrect2(Interface & interface)
{
interface.open(2);
interface.close();
interface.open(1);
interface.close();
interface.open(3);
interface.close();
}
Код: Выделить всё
void actWrong(Interface & interface)
{
interface.open(1);
interface.open(2);
interface.open(3);
interface.close();
interface.close();
interface.close();
}
Требуются надежные тесты
Я теперь хочу написать тесты с помощью GTest/GMock таким образом, чтобы тесты были устойчивы к рефакторингу с изменением порядка. Если быть точным, тесты должны:
- все равно пройти успешно, если я изменю порядок open()/пары. (см. actCorrect() и actCorrect2())
Код: Выделить всё
close()
- break, если open()/разрываются так, что они больше не являются последующими вызовами экземпляра интерфейса. (см. actWrong())
Код: Выделить всё
close()Пары
Чтобы выполнить требование 2, я мог бы использовать объект InSequence следующим образом:
Код: Выделить всё
MockInterface mock;
InSequence const seq;
EXPECT_CALL(mock, open(1));
EXPECT_CALL(mock, close());
EXPECT_CALL(mock, open(2));
EXPECT_CALL(mock, close());
EXPECT_CALL(mock, open(3));
EXPECT_CALL(mock, close());
act(mock);
Вместо этого я мог бы использовать объекты Sequence, например:< /p>
Код: Выделить всё
void expectOpenClose(MockInterface & mock, int const id)
{
Sequence const seq;
EXPECT_CALL(mock, open(id)).InSequence(seq);
EXPECT_CALL(mock, close()).InSequence(seq).RetiresOnSaturation();
}
TEST(Fixture, ActCorrect)
{
MockInterface mock;
expectOpenClose(mock, 1);
expectOpenClose(mock, 2);
expectOpenClose(mock, 3);
actCorrect(mock);
}
Лучшее, что я могу придумать, — это ввести состояние для отслеживания текущего идентификатора и выдать ошибку, если он не совпадает:
Код: Выделить всё
MockInterface mock;
bool isOpen{ false };
EXPECT_CALL(mock, open(_)).WillRepeatedly(InvokeWithoutArgs([&]()
{
if (isOpen)
{
throw std::runtime_error{ "Calling open() but is not closed" };
}
isOpen = true;
}));
EXPECT_CALL(mock, close()).WillRepeatedly(Invoke([&]()
{
if (!isOpen)
{
throw std::runtime_error{ "Calling close() but is not opened" };
}
isOpen = false;
}));
Код: Выделить всё
close()
Есть идеи, как добиться этого в рамках GMock? В идеале с помощью одной из операций секвенирования, например .InSequence() или .After().
Представленный здесь код можно просмотреть вживую в Compiler Explorer
Подробнее здесь: https://stackoverflow.com/questions/786 ... lock-order