Юнит-тестирование и google mock. Влад Лосев, google
DESCRIPTION
Владимир Лосев, GoogleЗакончил математико-механический факультет Санкт-Петербургского государственного университета в 1995 году. Работал в компаниях Motоrola, Fair Isaac и Yahoo. С 2008 года работает в Google, в группе, занимающейся вопросами повышения производительности инженеров.Тема докладаЮнит-тестирование и Google Mock.ТезисыВ модульных (юнит) тестах каждый элемент программы тестируется по отдельности, в изоляции от других. Такие тесты исполняются очень быстро, поэтому их можно запускать когда угодно, что позволяет отлавливать дефекты на самых ранних стадиях разработки. Однако для тестирования объекта в изоляции от других необходимо имитировать поведение связанных с ним объектов, что на C++ довольно утомительное занятие. Разработанная в Googlе библиотека для создания и использования mock-объектов — Google Mock — позволяет существенно упростить этот процесс и ускорить написание тестов. В докладе пойдет речь о принципах и возможностях библиотеки, примерах её использования и её внутреннем устройстве.TRANSCRIPT
Модульное тестирование и Google Mock
Владимир ЛосевGoogle
Yet Another Conference 2011Москва, 19 сентября
1
Agenda
1. Почему Google Mock?2. Как им пользоваться3. Примеры
2
Хочешь жить без багов?Спроси меня как!
• Ошибки снижают производительность• ...Если не чинить их быстро• Нужны автоматизированные тесты
o Быстрыеo Надёжныеo Точные
• Тестируем по одному модулю за раз
3
Изоляция модулей
• В программах на C++ абстрагируем взаимодействия между объектами через интерфейсы
• Используем тестовые двойники (stub-, mock-объекты, и т.д.) для имитации поведения соседей
• Используем метод внедрения зависимостей для добавления двойников
4
Внедрение зависимостейclass SocketInterface { public: virtual int Read(void* buffer, int size) = 0;};
class Server { public: Server(SocketInterface* socket) : socket_(socket) {} bool ReadRequest(string* result) { char buffer[BUFFER_SIZE]; int bytes_read; while ((bytes_read = socket->Read(buffer, BUFFER_SIZE)) > 0) result->append(buffer, buffer + bytes_read); return result->size() > 0; } private: SocketInterface* const socket_;};
5
class MockSocket : public SocketInterface { public: MockSocket(const char* buffer_to_read, int buffer_size) : buffer_to_read_(buffer_to_read_), size_(buffer_size), bytes_read_(0) { } virtual int Read(void* buffer, int n) { int actual_read = std::min(n, size_ - bytes_read_); memcpy(buffer, buffer_to_read_ + bytes_read_, actual_read); bytes_read_ += actual_read; return actual_read; } int bytes_read() const { return bytes_read_; }
private: const char* buffer_to_read_; int size_; int bytes_read_;};
Mock-объект, написанный вручную
6
Тест с таким объектом
TEST(Server_ReadRequest, ReadsCompleteRequest) { string data("1234567890123456789012345"); MockSocket mock_socket(data.data(), data.size());
Server server(&mock_socket); string buffer; EXPECT_TRUE(server.ReadRequest(&buffer)); EXPECT_EQ(data, buffer);}
• Проверяет только результат, а не взаимодействие
7
Google Mock• Google C++ Mocking Framework/Google Mock/gMock• Написан в 2007 г. Жаньонгом Ваном (Zhanyong Wan)• Код открыт в 2008 г.• Лежит на code.google.com/p/googlemock• Используется во многих проектах с открытым кодом
(Chromium, LLVM)
8
Нужна библиотека для написания тестов
• Хорошо интегрирован с Google Test• Лежит на http://code.google.com/p/googletest• Позволяет легко и быстро писать тесты:
#include <gtest/gtest.h>
TEST(MathTest, ArithmeticsStillWorks) { EXPECT_EQ(4, 2 * 2); }
9
Mock-объект на Google Mock class MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
10
Определение ожиданий class MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
EXPECT_CALL(mock_socket, Read(…))
11
Описние параметровclass MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
EXPECT_CALL(mock_socket, Read(_, Ge(25)))
12
Описание количества вызовов class MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
EXPECT_CALL(mock_socket, Read(_, Ge(25))) .Times(2)
13
Описание поведенияclass MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
string data("1234567890123456789012345"); EXPECT_CALL(mock_socket, Read(_, Ge(25))) .Times(2) .WillOnce(DoAll(CopyData(data), Return(data.size()))) .WillOnce(Return(0));
14
Полный тестclass MockSocket : public SocketInterface { public: MOCK_METHOD2(Read, int(void* buffer, int n));};
TEST(Server_ReadRequest, ReadsCompleteRequest) { MockSocket mock_socket; string data("1234567890123456789012345"); EXPECT_CALL(mock_socket, Read(_, Ge(25))) .Times(2) .WillOnce(DoAll(CopyData(data), Return(data.size()))) .WillOnce(Return(0));
Server server(&mock_socket); string result; EXPECT_TRUE(server.ReadRequest(&result)); EXPECT_EQ(data, result);}
15
Предикаты• ...на стероидах
o Проверяют условияo Описывают эти условияo Предоставляют объяснения
Примеры:
Простые: 42, _, Gt(32), NotNull()Составные: AllOf(m1, ...), AnyOf(m1, ...), Not(matcher)Для контейнеров: Contains(matcher), Each(matcher)ElementsAre(e1, ..., en), ElementsAreArray(arr)
• Можно определять собственные
16
Количество вызовов
• Указывается в методе Times():
EXPECT_CALL(mock, Foo()).Times(cardinality);
• Можно указывать:o 2o Exactly(2)o AtLeast(3)o AtMost(5)o Between(3, 4)
17
Порядок
• По умолчанию порядок вызовов не задаётся• Можно указывать полный или частичный порядок• Отсутствие циклов гаранировано
{ InSequence s; EXPECT_CALL(mock, A()); EXPECT_CALL(mock, B());}
Sequence s1, s2;EXPECT_CALL(mock, A()).InSequence(s1, s2);EXPECT_CALL(mock, B()).InSequence(s1);EXPECT_CALL(mock, C()).InSequence(s2);
18
Действия
• Определяются в методах WillOnce() и WillRepeatedly() • Определяют поведение mock-объекта• Статически типизированы (type-safe)
SetArrayArgument<>(), SetArg<>(), SaveArg<>(), Return(value), ReturnRef(object), Invoke(function), Invoke(object, method)
Составные: DoAll(action1, action2, ...)
• Можно определять самому• ON_CALL() определяет действие по умолчанию чтобы
можно было не указывать WillOnce или WillRepeatedly
19
Расширяем Google Mock – предикаты
MATCHER(IsEven, "") { return arg % 2 == 0; }
MATCHER_P(IsDivisibleBy, n, "") { return arg % n == 0;}
MATCHER_P2(IsSumOf, a, b, "") { return arg == a + b;}EXPECT_CALL(mock, Foo(IsSumOf(x, 7)));
• Не должны вызывать сторонних эффектов• Не должны содержать изменяемого состояния• Есть более продвинутое API для создания предикатов
20
Расширяем Google Mock – действия
ACTION(ChooseCallee) { if (arg0) arg1->f(); else arg2->g();}
ACTION_P(CopyData, data) { std::copy(data.data(), data.data() + data.size(), arg0);}
• Доступ к аргументам метода: arg0, arg1, ...• Есть более продвинутое API для создания действий
21
Пример: внедрение ошибок
TEST(FileReaderTest, HandlesConnectionClosed) { MockSocket mock_socket; MockServerManager mock_server_manager;
EXPECT_CALL(mock_socket, Read(_, Ge(25)) .WillOnce(SetErrnoAndReturn(EPIPE, -1)); EXPECT_CALL(mock_server_manager, OnConnectionLost());
Server server(&mock_socket, &mock_server_manager); EXPECT_FALSE(reader.ReadRequest(&result));}
22
Пример: быстрый сон
class WallClockInterface { public: virtual time_t GetWallTime() const = 0; virtual void SleepMilliseconds(time_t ms) = 0;};
class MockWallClock : public WallClockInterface { public: MOCK_CONST_METHOD0(GetWallTime, time_t()); MOCK_METHOD1(SleepMilliseconds, void());};
23
Пример: быстрый сонTEST(NetworkClientTest, WaitsAndRetries) { MockWallClock mock_clock; MockSocket mock_socket; InSequence s; EXPECT_CALL(mock_socket, Connect(_)) .WillOnce(Return(false)); EXPECT_CALL(mock_clock, GetWallTime()) .WillOnce(Return(1000)); EXPECT_CALL(mock_clock, SleepMilliseconds(2000)); EXPECT_CALL(mock_clock, GetWallTime()) .WillOnce(Return(3000)); EXPECT_CALL(mock_socket, Connect(_)) .WillOnce(Return(true));
NetworkClient client(&mock_clock, &mock_socket); EXPECT_TRUE(client.AttemptConnectWithRetry());}
24
Преимущества
• Проверяет взаимодействие между объектом и соседями• Позволяет писать быстрые тесты• Позволяет писать надёжные тесты• Упрощает тестирование кода обработки ошибок• Позволяет писать код, не имея соседей
25
Недостатки
• Mock-объекты компилируются медленно• Малопонятная диагностика
o Написан Google Mock Doctor, скрипт для интерпретации диагностики (GCC и LLVM)
• Mock-нужно писать вручнуюo Есть скрипт для генерации mock-классов из
определений интерфейсов или классов
26
Google Mock
http://code.google.com/p/googlemock
http://code.google.com/p/googlemock/wiki/Documentation
[email protected] (English)
27