【问题标题】:Easily writing mocks that hold state轻松编写保持状态的模拟
【发布时间】:2016-04-15 20:27:22
【问题描述】:

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

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

【问题讨论】:

    标签: c++ unit-testing mocking googletest googlemock


    【解决方案1】:

    我假设您正在测试Client 类 - 所以您真正需要的是存储ClientSocket 中注册的回调函数。通过SaveArg&lt;N&gt; 操作执行此操作。见代码:

    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 类 - 您可以收集所有回调。您可以使用这些捕获的回调来触发您的客户端类中的操作,如下面的测试用例所示:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多