如何用GMock模拟QUdpSocket测试ServerUDP类?解决绑定方法未触发问题
问题:使用GMock测试依赖QUdpSocket的ServerUDP类失败
我在ServerUDP类中实现了init方法,用于初始化QUdpSocket,现在想用GMock对该类做单元测试,完全不依赖Qt真实组件。
QAbstractSocket中bind方法声明:
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
我创建了Mock类和测试对象:
class QUdpSocketMock : public QUdpSocket { public: // MOCK_METHOD(bool, bind, (const QHostAddress&, quint16, BindMode)); MOCK_METHOD3(bind, bool(const QHostAddress&, quint16, BindMode)); }; class ServerUDPTest : public ::testing::Test { protected: QUdpSocketMock socketMock; ServerUDP serverUDP{socketMock}; };
测试用例期望bind返回false,但EXPECT_CALL提示bind从未被调用:
TEST_F(ServerUDPTest, TestCase) { EXPECT_CALL(socketMock, bind).WillOnce(Return(false)); EXPECT_EQ(Error_Code_T::ERROR, serverUDP.init()); }
ServerUDP类定义:
class ServerUDP : public QObject, public ServerInterface { Q_OBJECT public: explicit ServerUDP(QUdpSocket& socket_, QObject *parent = nullptr); virtual ~ServerUDP() {}; virtual Error_Code_T init(); virtual Error_Code_T start(); virtual Error_Code_T stop(); virtual Error_Code_T deinit(); public slots: virtual void readData(); private: QUdpSocket& socket; QString hostName; QString localHostAddress; quint16 port; ServerUDP() = delete; ServerUDP(ServerUDP&) = delete; ServerUDP& operator=(ServerUDP&) = delete; };
问题:为什么bind没被调用?如何不依赖Qt真实组件测试该类?是不是QUdpSocket是QObject导致的?
原因分析与解决方案
核心原因:QUdpSocket::bind不是虚函数
GMock只能mock虚函数或者基于抽象接口的方法。而Qt的QAbstractSocket::bind是普通成员函数,并非虚函数——你继承QUdpSocket创建的mock类中,用MOCK_METHOD3定义的bind并不会被ServerUDP调用,因为ServerUDP里的socket是QUdpSocket&类型,调用socket.bind(...)时会直接触发基类的真实方法,而非mock版本。
解决步骤:抽象套接字接口
要脱离Qt真实组件测试,必须对QUdpSocket做接口抽象,让ServerUDP依赖抽象接口而非具体类。
- 定义抽象接口类
创建包含ServerUDP所需QUdpSocket方法的纯虚接口:
class IUdpSocket : public QObject { Q_OBJECT public: virtual ~IUdpSocket() = default; virtual bool bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = QAbstractSocket::DefaultForPlatform) = 0; // 按需添加ServerUDP用到的其他方法,比如close()、state()等 };
- 修改ServerUDP依赖抽象接口
将ServerUDP中的QUdpSocket&替换为IUdpSocket&:
class ServerUDP : public QObject, public ServerInterface { Q_OBJECT public: // 构造函数参数改为抽象接口引用 explicit ServerUDP(IUdpSocket& socket_, QObject *parent = nullptr); virtual ~ServerUDP() {}; virtual Error_Code_T init(); // 其他方法保持不变... private: IUdpSocket& socket; // 替换为抽象接口引用 // 其他成员变量不变... };
- 创建Mock类实现抽象接口
用GMock实现IUdpSocket接口:
class UdpSocketMock : public IUdpSocket { Q_OBJECT public: MOCK_METHOD3(bind, bool(const QHostAddress&, quint16, QAbstractSocket::BindMode)); // 若ServerUDP用到其他方法,在此添加对应的MOCK_METHOD };
- 更新测试用例
使用新的Mock类编写测试:
class ServerUDPTest : public ::testing::Test { protected: UdpSocketMock socketMock; ServerUDP serverUDP{socketMock}; }; TEST_F(ServerUDPTest, TestCase) { // 用_匹配任意参数,也可指定具体值 EXPECT_CALL(socketMock, bind(_, _, _)).WillOnce(Return(false)); EXPECT_EQ(Error_Code_T::ERROR, serverUDP.init()); }
- 适配生产环境
在真实业务代码中,创建包装真实QUdpSocket的实现类:
class RealUdpSocket : public IUdpSocket { Q_OBJECT public: RealUdpSocket(QObject* parent = nullptr) : udpSocket(parent) {} bool bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = QAbstractSocket::DefaultForPlatform) override { return udpSocket.bind(address, port, mode); } private: QUdpSocket udpSocket; };
额外注意事项
- 因接口继承
QObject,Mock类和实现类都需添加Q_OBJECT宏,确保Qt的MOC工具能正确处理。 - 若
ServerUDP用到QUdpSocket的信号槽,需在IUdpSocket中声明对应信号和纯虚槽函数,Mock类可通过GMock的MOCK_SIGNAL或手动实现槽函数适配。
内容的提问来源于stack exchange,提问作者zenek martyniuk
相关产品推荐
相关产品推荐

