Reputation: 16442
I have a class that wraps Asio. It is meant to simulate communication over domain and tcp sockets but I'm at a loss as to automate unit tests. I looked at FakeIt but it only tests virtual methods, GoogleMocks suggests templating my code so I can then pass a MockAsio implementation for unit tests and the real Asio in production.
Are there any other ways to unit test network code? Fake a domain and tcp socket instead of running the whole stack? And if I go with GoogleMock, why use a class that uses GoogleMock and not my own implementation that does whatever I need?
Upvotes: 4
Views: 3710
Reputation: 146
I have recently ran into the same problem. Since an Asio service's methods (socket's read_some
for example) are generally not virtual, a simple dependency injection is out of question. As far as I understand, there are two possible approaches, both of them are discussed in the Google Mock Cook Book:
Discussed here.
This is the option @ruipacheco had already mentioned in his question.
This option requires templatizing your class, but it introduces the least code overhead.
If, for example, your class uses an Asio tcp socket, constructing an instance of it would look something like:
asio::io_context io_context;
MyClass<asio::ip::tcp::socket> my_class(io_context);
Discussed here.
This is more or less what @NiladriBose had suggested.
This option requires writing an Asio interface and an Asio concrete adapter, for every service type (socket, timer, etc...)! Nevertheless, it's the most generic and robust one (and it does not require templatizing your class, as the previous option did).
If, for example, your class uses an Asio tcp socket, constructing an instance of it would look something like:
asio::io_context io_context;
AsioSocket socket(io_context);
MyClass my_class(socket);
If your class uses multiple instances of Asio services (multiple sockets, timers, etc...), it would probably be better to create an abstract Asio services factory.
This factory would receive an io_context
in its constructor, and export make_socket
, make_timer
, etc... methods.
Then, constructing an instance of your class would look something like:
AsioFactory asio_factory(io_context);
MyClass my_class(asio_factory);
Finally, in regard to:
And if I go with GoogleMock, why use a class that uses GoogleMock and not my own implementation that does whatever I need?
See this to understand the difference between mock and fake objects. Generally speaking, I think that mock objects require much less effort. Also see this to figure out how to incorporate a Google mock class with a fake class.
Upvotes: 6
Reputation: 1905
I am assuming you want to mock out the ASIO wrapper class which is used by your application. If my assumption is correct then say the wrapper has an interface (oversimplified - but most mock frameworks require a pure abstract including gmock)-
class Iasio
{
virtual ~Iasio()
{
}
virtual void send(std::vector<unsigned char> dataToSend) = 0;
virtual std::vector<unsigned char > rcv() = 0;
};
Then you have two options- 1) mock using a mocking framework and in your unit test use the mock ( inject the mock into the classes that are using it using contructor or accessor injection). For this for each unit test scenario you would need to setup the mock object to return your expected data.
2) The first option sometimes can be more cumbersome than writing you own test mock , in such circumstances it is perfectly acceptable to write your own test mock giving you more control. I say more control because mock frameworks are general purpose and they can help in most common scenarios but complex scenarios can demand a bespoke test dummy/mock.
Upvotes: -1