Techniques for mocking in C Adam Badura software
Techniques for mocking in C++ Adam Badura software architect and developer at Nokia adam. f. badura@gmail. com
Agenda • Singleton and its problems • Passive Singleton to the rescue • External APIs • Other techniques
Singleton // singleton. hpp class singleton { public: static singleton& get(); private: static singleton obj; }; // singleton. cpp singleton: : obj{}; singleton& singleton: : get() { return obj; }
Singleton // singleton. hpp class singleton { public: static singleton& get(); private: static singleton* ptr; 1 }; // singleton. cpp singleton* singleton: : ptr{}; singleton& singleton: : get() { if(!ptr)2 ptr = new singleton(); 3 return *ptr; } Yes, normally you would use smart pointer. Yes, this is not thread safe. 3 Yes, the object is never delete-ed. But doing all this correctly would not fit on a slide! – you get the idea anyway… 1 2
Singleton // singleton. hpp class singleton { public: static singleton& get(); }; // singleton. cpp singleton& singleton: : get() { static singleton obj; return obj; }
Singleton • All those implementations share one problem… • …construction is in the same. cpp as rest of the code. • Which is problematic if testing code would like to use a test double. • So at the very least put construction code to a separate. cpp! • Production build will use it. • Testing build will use either it or a different one that constructs test double.
Singleton • However, another problem still exists… • …there is place only for one object – test double or real. • What if you would need more than one test double? • Sometimes a mock, • other times a stub, • and maybe even a real object at some point. • Building more than one target (binary) could be the answer… • …but there are other ways as well.
Passive Singleton 1 // singleton. hpp class singleton { public: singleton(); ~singleton(); static singleton& get(); }; 1 2 // singleton. cpp static singleton* ptr{}; 2 singleton: : singleton() { assert(!ptr); ptr = this; } singleton: : ~singleton() { assert(ptr == this); ptr = nullptr; } singleton& singleton: : get() { assert(ptr); return *ptr; } I made up this name myself – maybe it will catch… ; ) Anonymous namespace would be nice as well – however, no place for that on the slide…
Passive Singleton TEST_F(my_class, with_mock_singleton) { singleton_mock instance; /* … */ } TEST_F(my_class, with_real_singleton) { singleton instance; /* … */ }
Passive Singleton • Offers means to get the (single) object. • However, it doesn’t construct the instance. • Someone else must do it.
Passive Singleton • Now each test suite or even individual test case can • start from constructing the instance • Possibly a test double! • run as if with any singleton • destroy the instance at the end • With ordinary variables or smart pointers compiler will take care of this!
Passive Singleton • Bonus features • no persistent global state between test cases • trivial global variable • explicit construction/destruction order
External API • POSIX, Win. API, … • Typical approach – “link-time polymorphism”… • Tests use custom (usually stub) implementation on external API. • …and it’s problems • One size must fit all. • Usually no way to apply only to some functions.
OOP TTR! class sockets { public: virtual ~sockets() = default; virtual int accept(/*…*/) = 0; virtual int bind(/*…*/) = 0; /* … */ }; class sockets_real : public sockets { /* … */ }; class sockets_mock : public sockets { /* … */ };
OOP TTR! • Allows coexistence of multiple test doubles and real implementation. • Can be applied to only a subset of external API. • Dependency Injection vs. Singleton • If Singleton then why not Passive Singleton? • Often inconvenient naming.
Mixed solution • Keep normal calls in production code. • Use “link-time polymorphism” to provide stub in tests. • The stub implementation forwards to Passive Singleton. • Properties + Many test doubles possible. + Least impact on production code. + No virtual call in production code (if it matters to you…). − Hard to use real implementation in tests. − No way to apply only to some functions.
Other techniques • Link-time • --wrap in ld • Linker support for having test doubles that still could call real function. • Linux only! • Violate ODR (One Definition Rule) • “Override” function by providing local definition. • Problematic!
Other techniques • “Dynamic-linking polymorphism” • dlsym with RTLD_NEXT • “Override” function by providing local definition. • Use dlsym to retrieve real function address. • LD_LIBRARY_PATH • Inject your own fake library with its fake functions. • LD_PRELOAD • Similar idea to LD_LIBRARY_PATH. • More suited to inject only some functions. • Strongly environment-dependent! • Note that dynamic linking allows to work-around cases when use of test double blocks use of real functions.
Other techniques • Source level hackery • Preprocessor renames mocked functions. (-include) • Original name is now a test double only. • See Cpp. Con 2016: Atila Neves “Using C++14 to mock C functions". • Binary level hackery • Overwrite (at run-time) function code to jump to your test double. • Create mock object by synthesizing virtual table. • See C++Now 2017: Peter Bindels "Mocking C++".
Other techniques int main(int argc, char* argv[]) { return testable_main( argc, argv/*, std: : cout, std: : cerr, std: : cin*/ ); }
References 1. 2. 3. 4. 5. 6. 7. 8. 9. C++Now 2017: Peter Bindels "Mocking C++" • https: //github. com/dascandy/hippomocks Cpp. Con 2016: Atila Neves “Using C++14 to mock C functions" • https: //github. com/atilaneves/premock Cpp. Con 2017: Peter Sommerlad “Mocking Frameworks considered harmful” • http: //mockator. com/ Using Trompeloeil, a mocking framework for modern C++ - Bjorn Fahller [ACCU 2017] • https: //github. com/rollbear/trompeloeil Cpp. Con 2015: Matt Hargett “Advanced Unit Testing in C & C++” • https: //github. com/cgreen-devs/cgreen Google Test (+ Mock) • https: //github. com/googletest Stop Mocking, Start Testing Mocks & stubs by Ken Scambler Mocks Aren't Stubs
Image: Beyoncé - Single Ladies (Put a Ring on It) Idea: C++Now 2017: Peter Bindels "Mocking C++"
Q&A ?
- Slides: 23