轻松编写保持状态的模拟

Easily writing mocks that hold state

本文关键字:状态 模拟      更新时间:2023-10-16

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

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的函数。现在检查Socketwrite方法是否用这个字符串调用:……"

在另一个例子中,当我只有一个这样的功能时,我做了一些类似的事情:

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_CALL,从一个测试用例到另一个测试案例,这将非常轻微),我怀疑必须有更好的方法来做到这一点(不使用C风格的宏)。有什么模板魔法可以帮助吗?有什么可以让它更容易处理的吗?

我假设您正在测试Client类,所以您真正需要的是存储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);
    }
};

使用上面的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)
   //...
}