问题
I am writing a server/client based C++ application in Ubuntu with BSD socket. I am using Google C++ Test Framework as my unit test framework.
I wonder is there a way that I can create a server and client in my unit test, so I can test listen/accept for the server, and send/receive for both sides.
The question is, if I am going to test socket accept (after listening to a port) for the server, how can I have some client connecting to it in this test? Can I use multi-threading to have a client connect to the server being tested in the same TEST()
(or TEST_F()
) scope?
I can write a client and manually connect to the server under test for sure, but it defeat the purpose of automated unit test.
I read something about the Google Mock, it looks more like a sanity check for me (seeing which functions are being called, how many times, what got returned, etc.).
Please help, thanks.
回答1:
Ok, lets start with creating abstraction for network socket. We need this to be able to mock the calls to system functions. Start with something like this:
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 */
}
The above code doesn't abstract a lot of stuff and its bind to unix classes, because it is using sockaddr
and socklen_t
. You should also create abstraction for those two types to have platform independent code, but this depends on your design.
Not you need to create a concrete TCP/UDP class for using it in real application.
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_;
}
Phew :) let's move on to your class.
Lets assume your class is named A
and has method for testing method
.
Your class should either take Socket*
in constructor, or method
should take Socket*
as a parameter. Then in your testing code you can specify a mock.
class MockSock : public Socket {
public:
MOCK_METHOD2(connect, bool(const struct sockaddr*, socklen_t));
MOCK_METHOD2(accept, Socket*(struct sockaddr*, socklen_t*));
}
then just instantiate MockSock and pass it to A
or method
with proper EXPECT_CALL
values.
来源:https://stackoverflow.com/questions/41753538/how-to-unit-test-bsd-sockets