如何对 BSD 套接字进行单元测试
How to unit test BSD sockets
我正在使用 BSD 套接字在 Ubuntu 中编写基于 server/client 的 C++ 应用程序。我正在使用 Google C++ 测试框架作为我的单元测试框架。
我想知道有没有一种方法可以在我的单元测试中创建服务器和客户端,这样我就可以为服务器测试 listen/accept,并为双方测试 send/receive。
问题是,如果我要测试服务器的套接字接受(在监听端口之后),我怎样才能让一些客户端在这个测试中连接到它?我可以使用多线程让客户端连接到在同一 TEST()
(或 TEST_F()
)范围内被测试的服务器吗?
我可以编写一个客户端并手动连接到被测服务器,但它违背了自动化单元测试的目的。
我阅读了一些关于 Google Mock 的内容,它看起来更像是对我的健全性检查(查看正在调用哪些函数、调用了多少次、返回了什么等)。
请帮忙,谢谢。
好的,让我们开始为网络套接字创建抽象。我们需要它能够模拟对系统函数的调用。从这样的事情开始:
class Socket {
public:
virtual bool connect(const struct sockaddr *address, socklen_t address_len) = 0;
virtual Socket* accept(struct sockaddr *restrict address, socklen_t *restrict address_len) = 0;
/* more functions */
}
上面的代码并没有抽象很多东西,它绑定到 unix classes,因为它使用了 sockaddr
和 socklen_t
。您还应该为这两种类型创建抽象以具有平台独立代码,但这取决于您的设计。
您不需要创建具体的 TCP/UDP class 以便在实际应用中使用它。
class TCPSocket : public Socket {
public:
TCPSocket() {
socket_ = socket(PF_INET, SOCK_STREAM, 0);
if (socket_ == -1) {
/* handle errors */
}
}
TCPSocket(int sock) : socket_(sock) {}
bool connect(const struct sockaddr *address, socklen_t address_len) override {
return connect(socket_, address, address_len) == 0;
}
Socket* accept(struct sockaddr *restrict address, socklen_t *restrict address_len) override {
int s = accept(socket_, address, address_len);
if (s == -1) {
/* handle errors */
}
return new TCPSocket(s);
}
private:
int socket_;
}
呸 :) 让我们继续你的 class。
让我们假设您的 class 被命名为 A
并且有测试方法 method
。
您的 class 应该在构造函数中采用 Socket*
,或者 method
应该采用 Socket*
作为参数。然后在你的测试代码中你可以指定一个模拟。
class MockSock : public Socket {
public:
MOCK_METHOD2(connect, bool(const struct sockaddr*, socklen_t));
MOCK_METHOD2(accept, Socket*(struct sockaddr*, socklen_t*));
}
然后只需实例化 MockSock 并将其传递给 A
或 method
并使用适当的 EXPECT_CALL
值。
我正在使用 BSD 套接字在 Ubuntu 中编写基于 server/client 的 C++ 应用程序。我正在使用 Google C++ 测试框架作为我的单元测试框架。
我想知道有没有一种方法可以在我的单元测试中创建服务器和客户端,这样我就可以为服务器测试 listen/accept,并为双方测试 send/receive。
问题是,如果我要测试服务器的套接字接受(在监听端口之后),我怎样才能让一些客户端在这个测试中连接到它?我可以使用多线程让客户端连接到在同一 TEST()
(或 TEST_F()
)范围内被测试的服务器吗?
我可以编写一个客户端并手动连接到被测服务器,但它违背了自动化单元测试的目的。
我阅读了一些关于 Google Mock 的内容,它看起来更像是对我的健全性检查(查看正在调用哪些函数、调用了多少次、返回了什么等)。
请帮忙,谢谢。
好的,让我们开始为网络套接字创建抽象。我们需要它能够模拟对系统函数的调用。从这样的事情开始:
class Socket {
public:
virtual bool connect(const struct sockaddr *address, socklen_t address_len) = 0;
virtual Socket* accept(struct sockaddr *restrict address, socklen_t *restrict address_len) = 0;
/* more functions */
}
上面的代码并没有抽象很多东西,它绑定到 unix classes,因为它使用了 sockaddr
和 socklen_t
。您还应该为这两种类型创建抽象以具有平台独立代码,但这取决于您的设计。
您不需要创建具体的 TCP/UDP class 以便在实际应用中使用它。
class TCPSocket : public Socket {
public:
TCPSocket() {
socket_ = socket(PF_INET, SOCK_STREAM, 0);
if (socket_ == -1) {
/* handle errors */
}
}
TCPSocket(int sock) : socket_(sock) {}
bool connect(const struct sockaddr *address, socklen_t address_len) override {
return connect(socket_, address, address_len) == 0;
}
Socket* accept(struct sockaddr *restrict address, socklen_t *restrict address_len) override {
int s = accept(socket_, address, address_len);
if (s == -1) {
/* handle errors */
}
return new TCPSocket(s);
}
private:
int socket_;
}
呸 :) 让我们继续你的 class。
让我们假设您的 class 被命名为 A
并且有测试方法 method
。
您的 class 应该在构造函数中采用 Socket*
,或者 method
应该采用 Socket*
作为参数。然后在你的测试代码中你可以指定一个模拟。
class MockSock : public Socket {
public:
MOCK_METHOD2(connect, bool(const struct sockaddr*, socklen_t));
MOCK_METHOD2(accept, Socket*(struct sockaddr*, socklen_t*));
}
然后只需实例化 MockSock 并将其传递给 A
或 method
并使用适当的 EXPECT_CALL
值。