轻松编写保持状态的模拟

Easily writing mocks that hold state

我正在测试一些大量使用回调的代码,它看起来像这样:

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
};

但我需要能够写出以下形式的期望:"expect that this MockSocket object had its onA method called once, now call the function that was passed as an argument to onA. Now check that the write method of Socket was called with this string:...."

在另一种情况下,当我只有一个这种功能时,我做了类似的事情:

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 个 getter 和 5 个 setter + 所有 EXPECT_CALLs,从测试用例到测试用例会非常轻微),我怀疑必须有更好的方法来做到这一点(无需求助于 C 风格的宏)。是否有一些模板魔术可以提供帮助?有什么可以让它更容易处理的吗?

我假设您正在测试 Client class - 所以您真正需要的是存储 ClientSocket 中注册的回调函数。通过 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);

    }
};

通过上面的测试 class - 您可以收集所有回调。您可以使用这些捕获的回调在您的客户端 class 中触发操作,例如下面的测试用例:

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)
   //...
}