GoogleMockForDummiesRussian /

Filename Size Date modified Message
52.1 KB
README.md edited online with Bitbucket

Google C++ Mocking Framework для начинающих

Это перевод Google C++ Mocking Framework for dummies, выполненный demin, благополучно потерянный гуглом при переезде с code.google.com на github и вытащенный из кэша гугла

(Замечание: Если ваш компилятор выдает непонятные ошибки, попробуйте для начала обратиться к Google Mock Doctor.)

(Примечание перевода: На данный момент термин Mock-объект решено оставить без перевода и употреблять просто в английской транскрипции. Также термин Matcher решено оставить в оригинале, чтобы не использовать корявое "обнаружитель совпадений" или что-то в это роде.)

Что такое Google C++ Mocking Framework?

Когда вы создаете прототип или пишете тест, то порой не получается полагаться полностью на реальные объекты. Mock-объект реализует тот же интерфейс, что и реальный объект (поэтому он может использоваться как его замена), но позволяет во время выполнения задавать свое поведение (какие методы должны быть вызваны, в каком порядке и сколько раз, с какими аргументами, что должно быть результатом и т.д.).

Замечание: Mock-объекты легко перепутать с fake-объектами. Fake- и mock-объекты используются для совершенно разных целей в методологии разработки через тестирование (Test-Driven Development).

  • Fake-объект реализует полноценную логику класса, но обычно в немного урезанном виде (чтобы не задействовать сложные взаимосвязи, например, реальную базу данных), поэтому их нельзя использовать в реальном приложении. Например, файловая система, живущая полностью в памяти, вместо полноценной дисковой файловой системы, может быть примером fake-объекта.
  • Mock-объекты же запрограммированы в виде некоторых предположений для проверки вызова их методов извне и собственного поведения.

Если все выше сказанное пока еще не совсем понятно для вас, не волнуйтесь - главное запомните, что mock-объекты позволяют вам проверять взаимодействие между ними и кодом, которых их вызывает. Различие между fake- и mock-объектами станет более понятным, когда вы начнете их использовать.

Google C++ Mocking Framework (или просто Google Mock) является библиотекой (иногда мы также называем это "средой", так как это звучит модно) для создания mock-классов и работы с ними. Это работает также как jMock и EasyMock для Java, только в С++. На текущий момент Google Mock работает в паре с Google C++ Testing Framework. Мы надеемся в будущем поддержать работу с другими библиотеками тестирования.

Использование Google Mock состоит из трех основных шагов:

  • Определите mock-класс для интерфейса который вы хотите сымитировать. Это достигается использованием простых макросов для описания методов. Они создадут реализацию вашего mock-класса;
  • Создайте mock-объекты и задайте их предполагаемое использование и поведение, используя интуитивно понятный синтаксис;
  • Запустите код, который взаимодействует с этими mock-объектами. Google Mock автоматически отследит и проверит предписанное использование mock-объектов по мере выполнения.

Почему именно Google Mock?

Несмотря на то, что использование mock-объектов помогает удалить ненужные зависимости при тестировании и сделать его быстрым и надежным, работать вручную с ними в С++ затруднительно:

  • Кто-то должен создавать mock-классы. Эта работа обычно нудная и подвержена ошибкам. Неудивительно, что люди не любят это делать.
  • Качество mock-объектов, созданных вручную, может быть весьма непредсказуемым. Можно найти грамотно реализованные, но также полно созданных на скорую руку для решения конкретной проблемы и непригодных в других ситуациях.
  • Опыт, приобретенный после создания одного mock-объекта, оказывается бесполезным для следующего.

Для сравнения, программисты на Java и Python вооружены отличными инструментами для автоматизированного создания mock-объектов. Их использование является доказано эффективной и широко распространенной техникой в мире этих языков. Наличие правильного инструмента имеет большое значение.

Google Mock создан, чтобы помочь программистам на C++. Идея взята от jMock и EasyMock и реализована на C++. Если перечисленные ниже проблемы достают вас, Google Mock поможет их решить:

  • Вы застряли на стадии почти оптимальной архитектуры и хотите иметь более работоспособный прототип пока еще не совсем поздно, но создание прототипов на С++ нельзя назвать быстрым.
  • Ваши тесты работают медленно, так как зависят от множества библиотек или внешних ресурсов (например, базы данных).
  • Ваши тесты нестабильны, так как используют ненадежные ресурсы (например, сеть).
  • Вы ходите протестировать ваш код на предмет обработки ошибок (например, ошибка неверной контрольной суммы файла), но ошибки эти вызвать непросто.
  • Вы хотите убедиться, что ваш модуль правильно взаимодействует с другими модулями, но проследить это взаимодействие трудно, поэтому приходится использовать просто его результаты в конце работы.
  • Вы хотите сымитировать некоторые зависимости, но для них нет готовых mock-объектов, и вас явно не радуют mock-объекты, написанные вручную.

Мы предлагаем вам использовать Google Mock как:

  • средство разработки, позволяющее экспериментировать с разрабатываемым интерфейсом как можно раньше и чаще. Чем больше будет тестовых итераций, тем лучше будет архитектура!
  • средство тестирования для сокращения внешних зависимостей и проверки взаимодействия между вашим модулем и его связями.

С чего начать?

Использовать Google Mock очень просто! Внутри вашего исходного текста на С++ подключите <gtest/gtest.h> и <gmock/gmock.h>, и все готово.

Mock-объект Черепаха

Рассмотрим пример. Допустим, вы разрабатываете графическую программу, которая использует для рисования интерфейс в стиле языка Лого. Как вам убедиться что программа все делает правильно? Например, вы можете ее запустить и сравнить картинку на экране с эталонной, но признайтесь: подобное тестирование дорого и работает нестабильно (вы недавно обновили видеокарту, имеющую более навороченную систему сглаживания, и теперь вам необходимо обновить все эталонные изображения). Если бы все ваши тесты работали так, вам пришлось бы несладко. К счастью вы изучали метод внедрения зависимостей (Dependency Injection) и знаете, что делать: вместо прямого обращения из вашей программы к графическому интерфейсу вы можете добавить еще один уровень абстракции в виде, например, интерфейса Turtle:

class Turtle {
  ...
  virtual ~Turtle();
  virtual void PenUp() = 0;
  virtual void PenDown() = 0;
  virtual void Forward(int distance) = 0;
  virtual void Turn(int degrees) = 0;
  virtual void GoTo(int x, int y) = 0;
  virtual int GetX() const = 0;
  virtual int GetY() const = 0;
};

(Важно, чтобы деструктор класса Turtle обязательно был виртуальным, это влияет на все классы, унаследованные от него, а иначе деструктор унаследованного класса не будет вызван при удалении объекта через указатель на базовый класс, что может привести к утечкам памяти.)

Функции PenUp() и PenDown() позволяют управлять режимом, когда движение черепахи будет оставлять след, а когда нет. Передвижения же контролируются функциями Forward(), Turn(), и GoTo(). И наконец, функциями GetX() и GetY() вы можете получить текущую позицию черепахи.

Ваша программа обычно будет использовать настоящую реализацию этого интерфейса. В тестах же вы можете использовать mock-реализацию. Это позволяет вам легко проверять, какие функции рисования вызывает ваша программа, с какими аргументами и в каком порядке. Тесты, написанные таким образом, будут более надежными (например, на них не повлияет тот факт, что ваш новый компьютер выполняет сглаживание немного по-другому), простыми в понимании и дальнейшей модификации (смысл теста будет выражаться программным кодом, а не какими-то двоичными изображениями), и работать они будут гораздо, гораздо быстрее.

Создание Mock-класса

Хорошо, конечно, когда можно использовать mock-классы кем-то уже созданные. Если, однако, вам надо писать их самостоятельно, то расслабьтесь - Google Mock превратит это в увлекательную игру! (Ну почти.)

Определение mock-класса

Взяв Turtle в качестве примера, вы можете выполнить следующие шаги:

  • Унаследуйте класс MockTurtle от Turtle.
  • Выберите виртуальную функцию в классе Turtle. Посчитайте, сколько у нее аргументов.
  • В разделе public: дочернего класса напишите MOCK_METHODn(); (или MOCK_CONST_METHODn(); если это константная функция), где n количество аргументов функции. Если вы ошибетесь, то компилятор выдаст ошибку.
  • А вот сейчас самое интересное: перенесите имя функции в первый аргумент макроса MOCK_..., убрав ее из оригинального описания, затем перенесите все что осталось (после удаления имени функции) во второй аргумент макроса (на самом деле - это будет тип функции).
  • Повторите это для всех виртуальных функций, которые вы хотите включить в mock-класс.

После этого процесса вы получите что-то типа:

#include <gmock/gmock.h>  // Заголовочный файл Google Mock.
class MockTurtle : public Turtle {
 public:
  ...
  MOCK_METHOD0(PenUp, void());
  MOCK_METHOD0(PenDown, void());
  MOCK_METHOD1(Forward, void(int distance));
  MOCK_METHOD1(Turn, void(int degrees));
  MOCK_METHOD2(GoTo, void(int x, int y));
  MOCK_CONST_METHOD0(GetX, int());
  MOCK_CONST_METHOD0(GetY, int());
};

Вам не нужно определять эти mock-методы где-либо - макросы MOCK_METHOD* автоматически сгенерируют эти методы. Вот так все просто! Немного потренировавшись, вы можете выдавать mock-классы быстрее, чем ваша системы контроля версий будет их принимать.

Подсказка: В принципе, если даже это вам делать неохота, то вам понравится скрипт gmock_gen.py в каталога scripts/generator/ библиотеки Google Mock (наше почтение проекту cppclean). Этот скрипт требует наличия Python 2.4 на вашей машине. Вы просто задаете в командной строке имя исходного файла на С++ и имя абстрактного класса, в нем определенного, и получаете на выходе определение mock-класса. Из-за сложности синтаксиса C++ этот скрипт иногда может не срабатывать, но когда он работает - это очень удобно. За более подробной информацией обратитесь к его документации.

Куда помещать mock-класс

Когда вы определили mock-класс, вам нужно решить, где поместить его определение. Некоторые люди помещают их в *_test.cc. Это удобно, когда тестируемый интерфейс или класс (например Foo) разрабатывается тем же человеком или командой. Иначе, если автор Foo изменит его, то ваш тест может перестать работать. (Нельзя ведь ожидать, что автор Foo исправит каждый тест, который его использует, не так ли?)

Поэтому обычно делают так: если вам нужно сделать mock-объект для класса Foo, который разрабатывается другими людьми, определите mock-класс в модуле Foo (удобнее, чтобы это был подмодуль testing, так как можно легко отделить основной код от тестов), и назовите файл mock_foo.h. После этого все могут ссылаться на mock_foo.h из своих тестов. Если Foo изменится, то надо будет подправить только одну копию MockFoo и только те тесты, которые зависят от измененных методов Foo.

Есть и другой способ это сделать: можно ввести дополнительный уровень абстракции FooAdaptor над классом Foo и использовать его интерфейс. Так как тут уже вы являетесь автором FooAdaptor, будет намного проще следовать возможным изменениям в классе Foo. Изначально работы, конечно, больше, но правильно выбранный интерфейс FooAdaptor сделает код более простым и понятным (в долгосрочной перспективе), так как можно сделать FooAdaptor более подходящим конкретно для ваших нужд, нежели сам класс Foo.

Использование Mock-объектов в тестах

Когда mock-класс уже имеется, все просто. И его использование может выглядеть так:

  • Импортируйте необходимые вам имена из Google Mock из пространства имен testing, чтобы можно было использовать эти имена без дополнительного префикса (Это надо сделать только один раз для каждого файла. Помните, что пространства имен являются отличным механизмом и значительно упрощают жизнь.).
  • Создайте mock-объекты.
  • Задайте их предполагаемое использование (сколько раз методы будут вызываться, с какими аргументами, и что при этом надо делать и т.д.).
  • Выполните код, использующий эти mock-объекты. Дополнительно можно использовать утверждения (assertions) из библиотеки Google Code. Если mock-объект был вызван более чем предполагаемое количество раз или с неверными аргументами, вы немедленно получите сообщение об ошибке.
  • Когда mock-объект уничтожается, Google Mock автоматически проверит, все ли заданные предположения для этого объекта выполнены.

Например:

#include "путь/к/mock-turtle.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using ::testing::AtLeast;                     // #1

TEST(PainterTest, CanDrawSomething) {
  MockTurtle turtle;                          // #2
  EXPECT_CALL(turtle, PenDown())              // #3
      .Times(AtLeast(1));

  Painter painter(&turtle);                   // #4

  EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}                                             // #5

int main(int argc, char** argv) {
  // Данная строка должна быть выполнена для инициализации Google Mock
  // и Google Test) до запуска тестов.
  ::testing::InitGoogleMock(&argc, argv);
  return RUN_ALL_TESTS();
}

Как вы догадались, этот тест проверяет, что PenDown() должен вызываться как минимум один раз. Если объект painter не вызвал этот метод, то тест выдаст сообщение об ошибке:

path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.

Подсказка 1: Если вы запускаете тест из буфера Emacs, то нажав <Enter> на номере строки в сообщении об ошибке, вы перейдете прямо к месту в тестах, где происходит сбой.

Подсказка 2: Если mock-объект никогда не удаляется, то финальная проверка не сработает. Поэтому неплохо бы использовать какой-нибудь контроль динамической памяти в тестах, если mock-объекты создаются в куче.

Важное замечание: Google Mock требует установки ожиданий до вызова функций mock-класса, иначе поведение программы будет неопределенным. В частности, нельзя чередовать вызовы EXPECT_CALL() и вызовы методов mock-класса.

Это значит, что EXPECT_CALL() должен пониматься как ожидаемый вызов в будущем, а не как вызов, который уже произошел. Почему Google Mock работает таким образом? А потому, что предоварительное описание ожидаемого поведения позволяет Google Mock сообщать об ошибках немедленно по мере их возникновения, пока состояние программы (стек, например) все еще доступно. Отладка в этом случае становится намного проще.

Правда, рассмотренный тест довольно искусственный и особо важной работы не делает. Вы можете легко сделать тоже самое и без Google Mock. Однако, вы скоро поймете, что Google Mock позволяет делать гораздо больше.

Использование Google Mock с другими библиотеками тестирования

Если вы используете что-то отличное от Google Test (например, CppUnit или CxxTest), то просто подправьте функцию main() в предыдущем примере:

int main(int argc, char** argv) {
  // Данная строка устанавливает Google Mock бросать исключение при ошибке,
  // которое может быть использовано вашей библиотекой тестирования как ошибка.
  ::testing::GTEST_FLAG(throw_on_failure) = true;
  ::testing::InitGoogleMock(&argc, argv);
  // ... все, что требуется для вашей библиотеки тестирования ...
}

Установка предположений

Ключевыми моментом в использовании mock-объекта является установка правильных предположений о нем. Если предположения слишком строгие, то ваш тест может дать сбой из-за не относящихся к вашему классу изменений. Если наоборот, слишком слабые, то можно пропустить ошибку. Надо стараться, чтобы ваш тест ловил те ошибки, которые могут потенциально возникнуть. Google Mock обеспечивает вас всем необходимым, чтобы сделать это "как надо".

Общий синтаксис

В Google Mock мы используем макрос EXPECT_CALL() для установки предположений о поведении mock-объекта. Выглядит это так:

EXPECT_CALL(mock_object, method(matchers))
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action);

Этот макрос имеет два аргумента: mock-объект и его метод с аргументами. Необходимо отметить, что эти два аргумента макроса разделены запятой (,), а не точкой (.). (Почему запятая? Из-за технических соображений.)

За макросом могут следовать необязательные операторы для задания дополнительной информации о текущем предположении. Мы обсудим, как каждый из них работает чуть ниже.

Общий синтаксис разработан так, что быть похожим просто на английский язык. Например, несложно догадаться, что:

using ::testing::Return;...
EXPECT_CALL(turtle, GetX())
    .Times(5)
    .WillOnce(Return(100))
    .WillOnce(Return(150))
    .WillRepeatedly(Return(200));

означает, что метод GetX() объекта turtle должен быть вызван пять раз. Первый раз он должен вернуть 100, второй раз 150 и 200 все последующие разы. Иногда это называют синтаксисом Предметно-ориентированного языка программирования.

Замечание: Почему мы используем для этого макрос? Тут две причины, первая, что надо делать предположения видимыми в тексте (либо через grep, либо просто человеческому глазу), и вторая, что это позволяет Google Mock определить имя файла и номер строки, где произошел сбой теста. Это значительно упрощает отладку.

Matchers: аргументы макроса EXPECT_CALL()

Когда функция mock-объекта имеет аргументы (формальные параметры), мы просто задаем их желаемые значения, например:

// Ожидаем, что `turtle` сдвинется вперед на 100 единиц.
EXPECT_CALL(turtle, Forward(100));

Иногда нет необходимости указывать все слишком конкретно (Помните, что бывает, когда тесты слишком строгие?) Чрезмерная конкретность делает тесты неустойчивыми и несколько затуманивает саму цель тестирования. Мы призываем фокусироваться исключительно на том, что тестируется - ни больше, ни меньше. Если вам интересно просто проверить, вызывается ли Forward(), и неважно с каким аргументом, напишите _ в качестве аргумента, что будет означать "все что угодно".

using ::testing::_;
...
// Expects the turtle to move forward.
// Ожидаем, что `turtle` сдвинется вперед.
EXPECT_CALL(turtle, Forward(_));

_ - это то, что мы называем matcher'ы. Matcher - это предикат, который используется чтобы поверить, является ли его аргумент тем, чем мы ожидаем. Вы можете использовать matcher'ы внутри макроса EXPECT_CALL() везде, где подразумевается аргумент функции.

Список встроенных matcher'ов можно найти в CheatSheet. Например, Ge (больше или равно):

using ::testing::Ge;...
EXPECT_CALL(turtle, Forward(Ge(100)));

Данный код проверяет, что объекту turtle будет сказано продвинуться вперед как минимум на 100 единиц.

Количество повторений: сколько раз метод может быть вызван?

В первом примере мы использовали Times() сразу после вызова EXPECT_CALL(). Мы назвали его аргумент количеством повторений, и он означает - сколько раз этот вызов должен произойти. Это дает нам возможность повторять ожидания любое количество раз без повторного их написания. Также важно, что количество повторений может быть нечетким, как и обычный matcher, что позволяет сформулировать цель теста максимально точно.

Интересным является случай, когда написано Times(0). Как вы, может быть, успели догадаться - это значит, что функция вообще не должна быть вызвана с указанными аргументами, и Google Mock сгенерирует ошибку, если вдруг функция-таки была ошибочно вызвана.

Мы видели использование AtLeast(n) как пример нечеткого количества повторений. Можно обратиться к CheatSheet для получения полного списка функций задания количества повторений.

В принципе, вызов Times() может быть опущен. Если Times() не используется в явном виде, то Google Mock установит количество повторений по умолчанию по следующим правилам:

  • Если ни WillOnce() ни WillRepeatedly() не используются в EXPECT_CALL(), то подразумевается вызов Times(1).

  • Если используется WillOnce() n раз, но WillRepeatedly() не используется, и n >= 1, то подразумевается Times(n).

  • Если используется WillOnce() n раз и один раз WillRepeatedly(), и n >= 0, то подразумевается Times(AtLeast(n)).

Вопрос: Как вы думаете, что произойдет, если функция должна быть вызвана дважды, но реально вызвана четыре раза?

Действия: что делать, если...

Напомним, что mock-объект не имеет реальной работающей логики. Мы, как пользователи, просто задаем, что делать, когда вызываются методы mock-объекта. В Google Mock это делается очень просто.

Для начала, если возвращаемое методом mock-объекта значение является встроенным типом или указателем, функция выполняет действие по умолчанию (void функция просто вернет управление, bool функция вернет false, остальные функции вернут 0). Если ничего специально не указывать, то будет использоваться такая логика.

Второе, если функция mock-объекта не имеет действия по умолчанию, или действие по умолчанию вам не подходит, то необходимо задать, что должна возвращать функция при каждом срабатывании WillOnce(), за которым может следовать WillRepeatedly(). Например:

using ::testing::Return;...
EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillOnce(Return(300));

В данном примере turtle.GetX() будет вызвана в точности три раза (Google Mock предполагает это из количества использованных WillOnce(), так как мы явно не указали это через Times()), и должна будет вернуть 100, 200 и 300 соответственно.

using ::testing::Return;...
EXPECT_CALL(turtle, GetY())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillRepeatedly(Return(300));

означает, что turtle.GetY() будет вызвана как минимум дважды (Google Mock знает это, так как мы использовали WillOnce() дважды с последующим WillRepeatedly(), а не явно указали это через Times()), и должна вернуть 100 первый раз, затем 200 во второй раз и 300 все последующие разы.

Конечно, если просто написать Times(), то Google Mock не будет делать никаких предположений о количестве повторений, так как их число указано явно. А что если, число, которое вы указали в Times() больше, чем число использованных WillOnce()? В этом случае WillOnce() будет использована необходимое дополнительное количество раз, и Google Mock будет использовать действие по умолчанию для каждого такого вызова (если, конечно, вы не использовали WillRepeatedly().).

Что еще мы можем использовать в WillOnce() кроме Return()? Вы можете вернуть ссылку через ReturnRef(variable), или задействовать одну из готовых действий из CheatSheet.

Важное замечание: Оператор EXPECT_CALL() вычисляется только один раз, несмотря на то, что действие может выполняться несколько раз. Поэтому следует помнить о побочных эффектах. Например, данный код не будет делать то, что мы, возможно, ожидаем:

int n = 100;
EXPECT_CALL(turtle, GetX())
    .Times(4)
    .WillOnce(Return(n++));

Вместо выдачи последовательных значений 100, 101, 102, ..., и т.д., эта функция будет всегда возвращать 100, так как n++ будет вычислено только один раз. Аналогично, Return (new Foo) создаст новый объект Foo, когда EXPECT_CALL() будет выполнен, и будет возвращать тот же самый указатель каждый раз. Если вы хотите, чтобы побочный эффект случался каждый раз, то надо создать свой тип действия. Как это сделать мы расскажем в CookBook.

Время для еще одной загадки! Как вы думаете, что это значит?

using ::testing::Return;...
EXPECT_CALL(turtle, GetY())
    .Times(4)
    .WillOnce(Return(100));

Понятно, что turtle.GetY() должна быть вызвана четыре раза. Но если вы думаете, что каждый раз будет возвращаться значение 100, то подумайте еще раз! Помните, что WillOnce() используется только один раз, и после будет применяться действие по умолчанию. Получается, что правильный ответ такой: первый раз turtle.GetY() вернет 100, но все последующие разы будет возвращаться 0, так как возвращение 0 - это действие по умолчанию для функции типа int.

Использование множественных предположений

До сих пор мы использовали в примерах только одиночные предположения. В реальности, вы можете задавать предположения для нескольких методов mock-объекта, которые будут принадлежать разным mock-объектам.

По умолчанию, когда вызывается метод mock-объекта, Google Mock будет искать заданные предположения в обратном их определению порядке до тех пор, пока не будет найдено предположение, совпадающие по аргументам (по аналогии с "новое правило может перекрывать предыдущие"). Если найденное ожидание не может принять более вызовов, то возникнет ошибка превышения допустимого количества вызовов. Например:

using ::testing::_;...
EXPECT_CALL(turtle, Forward(_));  // #1
EXPECT_CALL(turtle, Forward(10))  // #2
    .Times(2);

Если Forward(10) вызван три раза подряд, то третий вызов приведет к ошибке, так как ожидание #2 может принять только два вызова. Однако, если третий вызов будет не Forward(10), а Forward(20), то ошибки не будет, так как этот вызов будет обработан предположением #1.

Заметка на полях: Почему Google Mock ищет утверждения в обратном порядке? Причина в том, что это позволяет пользователю задавать ожидания по умолчанию в конструкторе mock-объекта или при инициализации тестового класса (test fixture), и затем настраивать mock-объект под конкретный случай в теле самого теста. Так что, если вы имеете два предположения для одного метода, вы захотите поместить более специализированный вариант после других, или более специализированное правило будет перекрыто более общим, следующим за ним.

Упорядоченные и неупорядоченные вызовы

По умолчанию, предположения могут совпадать с вызовом, даже если предыдущие предположения не были удовлетворены. Другими словами, вызовы не обязаны происходить в порядке задания предположений.

Иногда вам может понадобиться, чтобы все предположения срабатывали в установленном порядке. Для этого нужно сделать следующее:

using ::testing::InSequence;...
TEST(FooTest, DrawsLineSegment) {
  ...
  {
    InSequence dummy;

    EXPECT_CALL(turtle, PenDown());
    EXPECT_CALL(turtle, Forward(100));
    EXPECT_CALL(turtle, PenUp());
  }
  Foo();
}

При создании объекта типа InSequence все предположения в текущем блоке объединяются в последовательность и должны происходить одно за другим. Так как упорядочивание обеспечивают конструтор и деструктор данного объекта, то его имя роли не играет.

В данном примере мы проверям, что Foo() вызывает три раза перечисленные функции в том порядке, в котором записаны сами предположения. Иначе произойдёт ошибка.

(А что если, вам необходимо задать порядок вызова только для определенных вызовов, но не всех? Можно ли задать нужный порядок только частично? Ответ... да! Потерпите немного и найдете ответ в CookBook).

Все предположения являются "липкими", если не задано обратное

Небольшая проверка того, как вы уже можете использоваться Google Mock. Как бы вы проверили, что turtle будет сказано переместиться в начало координат в точности два раза, и вы хотите проигнорировать все остальные вызовы?

После того, как напишете свой ответ, посмотрите на наш (но решите сначала сами, не подсматривая!):

using ::testing::_;...
EXPECT_CALL(turtle, GoTo(_, _))  // #1
    .Times(AnyNumber());
EXPECT_CALL(turtle, GoTo(0, 0))  // #2
    .Times(2);

Предположим, что turtle.GoTo(0, 0) вызывается дважды. На третий раз Google Mock проверит, что аргументы совпадают с предположением #2 (напомним, что мы всегда выбираем последнее предположение из списка подходящих). Теперь, так как мы ожидаем только два вызова, Google Mock сообщит об ошибке. В главе "Использование множественных предположений" мы объяснили, почему это происходит.

В данном примере видно, что предположения Google Mock по умолчанию "липкие" в том смысле, что они остаются активными даже после достижения верхней границы по количеству выполнений. Это важное правило, которое стоит запомнить. Оно затрагивает смысл задания предположений, и также отличается от аналогичных правил в других библиотеках для создания mock-объектов (Почему мы сделали так? Потому, что мы уверены, что наши правила более понятны для большего числа типичных случаев.)

Просто? Проверим, как хорошо вы все поняли: что делает данный пример?

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
  EXPECT_CALL(turtle, GetX())
      .WillOnce(Return(10*i));
}

Если вы думаете, что turtle.GetX() должен быть вызван n раз и вернет 10, 20, 30, ... и т.д., то подумайте еще раз! Проблема в том, что, как мы сказали, предположения по умолчанию "липкие". Так что при втором вызове turtle.GetX() последний (самый последний) EXPECT_CALL() даст совпадение, что немедленно приведет к ошибке превышения верхней границы. В целом, данный пример весьма бесполезен.

Правильный способ задать предположения для turtle.GetX(), чтобы возвращались значения 10, 20, 30 и т.д. - это явно указать, что данное утверждение "нелипкое". Другими словами, предположение должно становиться неактивным сразу после первого срабатывания:

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
  EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(10*i))
    .RetiresOnSaturation();
}

В данном конкретном случае мы также ожидаем, что вызовы будут проходить в определенном порядке, поэтому мы должны выполняемые действия подогнать под этот порядок. Для этого явно зададим их последовательность:

using ::testing::InSequence;
using ::testing::Return;
...
{
  InSequence s;

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

Кстати, другая ситуация, когда предположение может не быть "липкими" - это когда за его вызовом срабатывает другое предположение в последовательности. В этом случае предположение автоматически становится неактивным (и больше никогда не сработает).

Неиспользуемые вызовы

Mock-объект может иметь множество методов, и далеко не все из них важны для тестирования. Например, для некоторых тестов не важно, сколько раз вызываются GetX() и GetY().

В Google Mock, если какой-то метод вам не нужен, вы можете просто забыть про него. Если произойдет вызов этого метода, вы увидите предупреждение в отчете, но ошибкой это не будет.

Что дальше?

Поздравляем! Вы узнали достаточно, чтобы начать работать с Google Mock. Теперь вы, возможно, захотите присоединиться к группе googlemock и уже начать писать тесты с помощью Google Mock - у вас все получится! Кстати, предупреждаем, это затягивает.

Если вы чувствуете, что как-то пошло, то приступайте к CookBook. Там вы узнаете о множестве дополнительных возможностей Google Mock и однозначно повысите уровень наслаждения от тестирования.