I have a class which calls getaddrinfo for DNS look ups. During testing I want to simulate various error conditions involving this system call. What's the recommended method for mocking system calls like this? I'm using Boost.Test for my unit testing.
The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies. In mocking, the dependencies are replaced by closely controlled replacements objects that simulate the behavior of the real ones.
Mocking is done when you invoke methods of a class that has external communication like database calls or rest calls. Through mocking you can explicitly define the return value of methods without actually executing the steps of the method.
What is Database Mocking? Database Mocking is a technique that allows you to set the desired database state (for different tables) in your tests to let specific data sets ready for future test execution.
In this case you don't need to mock getaddrinfo
, rather, you need to test without relying on its functionality. Both Patrick and Noah have good points but you have at least two other options:
Since you already have your object in a class, you can subclass to test. For example, assume the following is your actual class:
class DnsClass { int lookup(...); }; int DnsClass::lookup(...) { return getaddrinfo(...); }
Then, for testing, you would subclass like this:
class FailingDnsClass { int lookup(...) { return 42; } };
You can now use the FailingDnsClass
subclass to generate errors but still verify that everything behaves correctly when an error condition occurs. Dependency Injection is often your friend in this case.
NOTE: This is quite similar to Patrick's answer but doesn't (hopefully) involve changing the production code if you aren't already setup for dependency injection.
In C++, you also have link-time seams which Michael Feathers describes in Working Effectively with Legacy Code.
The basic idea is to leverage the linker and your build system. When compiling the unit tests, link in your own version of getaddrinfo
which will take precedence over the system version. For example:
test.cpp:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <iostream> int main(void) { int retval = getaddrinfo(NULL, NULL, NULL, NULL); std::cout << "RV:" << retval << std::endl; return retval; }
lib.cpp:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res ) { return 42; }
And then for testing:
$ g++ test.cpp lib.cpp -o test $ ./test RV:42
Look up patterns for "Dependency Injection".
Dependency Injection works like this: instead of calling getaddrinfo directly in your code, the code uses an interface that has a virtual method "getaddrinfo".
In real-life code, the caller passes an implementation of the interface that maps the virtual method "getaddrinfo" of the interface to the real ::getaddrinfo function.
In unit tests, the caller passes an implementation that can simulate failures, test error conditions, ... to be short: mock anything you want to mock.
EDIT: Read "Working effectively with legacy code" of Michael Feathers for more tips.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With