Юнит-тестирование и Google Mock. Влад Лосев, Google

14,492 views

Published on

Владимир Лосев, Google

Закончил математико-механический факультет Санкт-Петербургского государственного университета в 1995 году. Работал в компаниях Motоrola, Fair Isaac и Yahoo. С 2008 года работает в Google, в группе, занимающейся вопросами повышения производительности инженеров.

Тема доклада
Юнит-тестирование и Google Mock.

Тезисы
В модульных (юнит) тестах каждый элемент программы тестируется по отдельности, в изоляции от других. Такие тесты исполняются очень быстро, поэтому их можно запускать когда угодно, что позволяет отлавливать дефекты на самых ранних стадиях разработки. Однако для тестирования объекта в изоляции от других необходимо имитировать поведение связанных с ним объектов, что на C++ довольно утомительное занятие. Разработанная в Googlе библиотека для создания и использования mock-объектов — Google Mock — позволяет существенно упростить этот процесс и ускорить написание тестов. В докладе пойдет речь о принципах и возможностях библиотеки, примерах её использования и её внутреннем устройстве.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
14,492
On SlideShare
0
From Embeds
0
Number of Embeds
9,340
Actions
Shares
0
Downloads
38
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • Finding and fixing bugs takes significant portion of programmer's time, killing productivity.
  • 18 lines
  • * Copy Data is magic, explained later, we have more general action
  • * gmock takes care of the rest * checks for violation and outputs precise diagnostic messages * WILL MARK TEST AS FAILED * checks are done as soon as possible, so can drop into debugger at THAT MOMENT
  • Mention the matcher library and custom matchers.
  • * If called too many times, gmock will complain when amount is exceeded * if called too few times, gmock will complain when the mock is destroyed * Custom cardinalities possible but have never been requested
  • Can define any acyclic dependency graph with it
  • Type safe: performing incompatible operations on arguments or returning incompatible type will cause build error
  • * More advanced API for developing matchers is available
  • * First one will not compile if the method has less than 3 arguments * Can have even more advanced 
  • * Imagine a client that tries an operation and has to repeat it after 2 seconds if it fails
  • * Note: we never have defined actual socket class
  • * Google Mock doctor 
  • Юнит-тестирование и Google Mock. Влад Лосев, Google

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

    ×