Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD, Unit Test and architectural changes

I'm writing an RPC middleware in C++. I have a class named RPCClientProxy that contains a socket client inside:

class RPCClientProxy {
  ...
  private:
    Socket* pSocket;
  ...
}

The constructor:

RPCClientProxy::RPCClientProxy(host, port) {
  pSocket = new Socket(host, port);
}

As you can see, I don't need to tell the user that I have a socket inside.

Although, to make unit tests for my proxies it would be necessary to create mocks for sockets and pass them to the proxies, and to do so I must use a setter or pass a factory to the sockets in the proxies's constructors.

My question: According to TDD, is it acceptable to do it ONLY because the tests? As you can see, these changes would change the way the library is used by a programmer.

like image 512
Leandro Avatar asked Jan 23 '23 00:01

Leandro


2 Answers

I don't adhere to a certain canon i would say if you think you would benefit from testing through a mock socket the do it, you could implement a parallel constructor

RPCClientProxy::RPCClientProxy(Socket* socket) 
{
   pSocket = socket
}

Another option would be to implement a host to connect to for testing that you can configure to expect certain messages

like image 85
Harald Scheirich Avatar answered Jan 28 '23 22:01

Harald Scheirich


What you describe is a perfectly normal situation, and there are established patterns that can help you implement your tests in a way that won't affect your production code.

One way to solve this is to use a Test Specific Subclass where you could add a setter for the socket member and use a mock socket in the case of a test. Of-course you would need to make the variable protected rather than private but that's probably no biggie. For example:

class RPCClientProxy 
{
    ...
    protected:
    Socket* pSocket;
    ...
};

class TestableClientProxy : public RPCClientProxy 
{
    TestableClientProxy(Socket *pSocket)
    {
        this->pSocket = pSocket;
    }
};

void SomeTest()
{
    MockSocket *pMockSocket = new MockSocket(); // or however you do this in your world.
    TestableClientProxy proxy(pMockSocket);
    ....
    assert pMockSocket->foo;
}

In the end it comes down to the fact that you often (more often than not in C++) have to design your code in such a way as to make it testable and there is nothing wrong with that. If you can avoid these decisions leaking out into the public interfaces that may be better sometimes, but in other cases it can be better to choose, for example, dependency inject through constructor parameters above say, using a singleton to provide access to a specific instance.

Side note: It's probably worth taking a look through the rest of the xunitpatterns.com site: there are a whole load of well established unit-testing patterns to understand and hopefully you can gain from the knowledge of those who have been there before you :)

like image 20
jkp Avatar answered Jan 28 '23 21:01

jkp