0

I am trying to write unit tests for a socket app. I am using the <sys/socket.h> header. In order to test a method, I should mock the socket free functions, so the method is tested in isolation from its dependencies. Here is my attempt, following this instructions:

Test file:

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "path/to/ClientConectionHeader"
#include <sys/socket.h>

using ::testing::AtLeast;

class EstablishSocketInterface {
    public:
    virtual int establishSock(int domain, int type, int protocol) = 0;
};

class EstablishSocket : public EstablishSocketInterface {
    public:
    int establishSock(int domain, int type, int protocol) override {
        return socket(domain, type, protocol);
    }
};

class MockSocket : public EstablishSocket {
    public:
    MOCK_METHOD(int, establishSock, (int domain, int type, int protocol));

};

TEST(ClientConnection, ClientConnectionConstructor) {
    MockSocket mockSocket;
    EXPECT_CALL(mockSocket, establishSock(AF_INET, SOCK_STREAM, 0))
        .Times(AtLeast(1));

    std::string serverIp="127.0.0.1";
    int port=49151;
    ClientConnection client(serverIp,port);
    
}

Header of the class I am testing:

#pragma once

class ClientConnection
{
public:
    ClientConnection(std::string& serverIp_input,int port_input);
  
private:
    int socketfd;
    const char* serverIp;
    int port;

    bool ExitProgram;   
};

Constructor definition:

ClientConnection::ClientConnection(std::string& serverIp_input, int port_input) {
    this->socketfd=socket(AF_INET, SOCK_STREAM, 0);
    if (this->socketfd < 0) {
        perror("socket");
    }
    this->serverIp = serverIp_input.c_str();
    this->port = port_input;
    this->ExitProgram = false;
}

The test fails with the following: Failure

Actual function call count doesn't match EXPECT_CALL(mockSocket, establishSock(2, SOCK_STREAM, 0))...
         Expected: to be called at least once
           Actual: never called - unsatisfied and active
[  FAILED  ] ClienConection.ClientConectionConstructor (0 ms)

The constructor calls the socket() function itself, does not use the mocked one. Dependency injection can work but requires changing the original code. I don't think this is the right approach. Also, to use EXPECT_EQ() to check whether the fields are valued the same as the arguments I pass to the constructor, I need getters, which also requires to be added, but are not needed in the 'production' code.

So how do I gtest my methods as I mock the socket(), send(), recv(), close() functions from the <sys/socket.h> header?

Thanks in advance!

headfxll
  • 35
  • 1
  • 1
  • 8
  • `establishSock` is not a free function, it's a class member function. You only create `mockSocket` and don't use it. How do you expect it can call the method? – 273K Mar 30 '23 at 07:38

1 Answers1

1

EstablishSocketInterface is never used in the scope of ClientConnection so establishSock is never called (no matter if we consider EstablishSocket or MockSocket. You need to use some form of the technique called 'dependency injection' (e.g. see Dependency injection with unique_ptr to mock). You'll need to wrap all methods (like socket(), send(), recv(), close()) in an interface and use this interface everywhere where you would normally used sys/socket.h directly.

There's also another technique - the one mentioned in the original question. It requires that all your code is templated so it's not that easy to use. If you insist on it though, then ClientConnection would need to be a templated class with socket as template parameter.

Quarra
  • 2,527
  • 1
  • 19
  • 27
  • Thanks, I did it with dependency injection and wrapped all methods in an interface. In the test, I am creating an object from the original class and pass the mocked interface to it. Is it necessary to have a parent class and both the 'production' and test classes to inherit, in order to use each one for its purpose – headfxll Mar 30 '23 at 15:17
  • Using the base class (the best would be a pure virtual class w/o any fields, just pure virtual methods as you did, just remember to add virtual dtor) is the way to go: keeps the code clean and forces the code to be oriented against abstractions, not implementations. It makes it more testable. I was using different approaches over the years, and common base class works best in 99% of situations (for me) – Quarra Mar 31 '23 at 05:43
  • Guess you mean the interface. I was talking about the main class "ClientConnection" that we inject it with the socketInterface. Do I have to mock the "ClientConnection" class itself ? or only if the "ClientConnection" is being injected into some other class. Thanks in advance, I will mark the question solved after your response. – headfxll Mar 31 '23 at 07:24
  • 1
    You're right - there's no need to mock `ClientConnection` yet. Once you start using it in other classes, it will need to be mocked for unit testing. Users of `ClientConnectionInterface` don't need to rely on `EstablishSocketInterface` anymore - users of `ClientConnection` will not be aware of what is used internally for the socket communication. This is the power of good interfaces and dependency injection - dependencies are clear and low-level details do not influence high-level abstractions. You'll need some kind of integration tests with non-mocked objects, but this is a different topic. – Quarra Mar 31 '23 at 08:17