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.
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.