Легко писать макеты, которые держат состояние

Я тестирую некоторый код, который интенсивно использует обратные вызовы, он выглядит примерно так:

class Client {
 public:
  Client(Socket socket) {
    socket->onA([this] {
        // call private Client methods....
      });
    socket->onB([this] {
        // call private Client methods...
      });
    // repeated for onC, onD, and onE
  }

  void Start();
  void Stop();
 private:
  // and almost all of the state is private
};

который почти полностью связывается с внешним миром посредством интерфейса, предоставляемого Socket, который выглядит как:

class Socket {
 public:
  void onA(std::function<void()> callBack) = 0;
  void onB(std::function<void()> callBack) = 0;
  void onC(std::function<void()> callBack) = 0;
  void onD(std::function<void()> callBack) = 0;
  void onE(std::function<void()> callBack) = 0;

  // various other public methods
};

Важно, чтобы я мог убедиться, что onX функции были вызваны, и получить доступ к аргументам, которые были переданы им (потому что Clientизменения состояния в ответ на уведомления от SocketМне нужно смоделировать их в моих тестах).

Я могу написать макет, который выглядит так:

class MockSocket : public Socket {
 public:
  MOCK_METHOD1(onA, void(std::function<void()> callBack));
  MOCK_METHOD1(onB, void(std::function<void()> callBack));
  // etc
};

но я должен быть в состоянии написать ожидания в форме: "ожидать, что это MockSocket объект имел свое onA вызванный метод, теперь вызовите функцию, которая была передана в качестве аргумента onA, Теперь проверьте, что write метод Socket был вызван с этой строкой:...."

В другом случае, когда у меня была только одна функция такого рода, я делал что-то вроде:

class MockSocket : public Socket {
 public:
  // as before

  void setCallbackForA(std::function<void()> callBack) {
    callbackForA = callBack;
  }

  void callCallbackForA() {
    callbackForA();
  }

  // etc

 private:
  std::function<void()> callbackForA;
  std::function<void()> callbackForB;
  // etc
};

Но это будет смешное количество шаблонов, чтобы сделать это (5 геттеров и 5 сеттеров + все EXPECT_CALLs, что очень немного от тестового к тестовому сценарию), и я подозреваю, что должен быть лучший способ сделать это (не прибегая к макросам в стиле C). Есть ли какая-то магия шаблонов, которая может помочь? Что-нибудь, чтобы сделать это немного легче иметь дело?

1 ответ

Решение

Я предполагаю, что вы тестируете Client класс - так что вам действительно нужно хранить функции обратного вызова, которые Client регистрируется в Socket, Сделать это SaveArg<N> действие. Смотрите код:

class ClientTest : public ::testing::Test
{
public:
    std::function<void()> onACallback;
    std::function<void()> onBCallback;
    //...
    MockSocket mockSocket;
    std::unique_ptr<Client> objectUnderTest;
    void SetUp() override
    {
       using ::testing::SaveArg;
       EXPECT_CALL(mockSocket, onA(_)).WillOnce(SaveArg<0>(&onACallback));
       EXPECT_CALL(mockSocket, onB(_)).WillOnce(SaveArg<0>(&onBCallback));
       //...
       objectUnderTest = std::make_unique<Client>(mockSocket);

    }
};

С классом Test выше - вы можете собрать все обратные вызовы. И вы можете использовать эти отслеживаемые обратные вызовы для запуска действий в вашем классе Client, как в тестовом примере ниже:

TEST_F(ClientTest, shallDoThisAndThatOnA)
{
   // here you put some expectations
   //...
   onACallback(); // here you trigger your Client object under test
   // now you make some assertions (post actions)
   //...
}
Другие вопросы по тегам