
Techniques for Mocking in C++ With Adam Badura
Explore techniques for mocking in C++ with software architect and developer Adam Badura. Learn about Singleton patterns, Passive Singletons, handling External APIs, and other useful techniques for effective C++ development.
Uploaded on | 0 Views
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
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.cpp singleton singleton::obj{}; // singleton.hpp class singleton { public: static singleton& get(); singleton& singleton::get() { return obj; } private: static singleton obj; };
Singleton // singleton.cpp singleton* singleton::ptr{}; // singleton.hpp class singleton { public: static singleton& get(); singleton& singleton::get() { if(!ptr)2 ptr = new singleton();3 return *ptr; } private: static singleton* ptr;1 }; 1Yes, normally you would use smart pointer. 2Yes, this is not thread safe. 3Yes, the object is never delete-ed. But doing all this correctly would not fit on a slide! you get the idea anyway
Singleton // singleton.cpp singleton& singleton::get() { static singleton obj; return obj; } // singleton.hpp class singleton { public: static singleton& get(); };
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 Singleton1 // singleton.cpp static singleton* ptr{};2 // singleton.hpp class singleton { public: singleton(); ~singleton(); static singleton& get(); }; singleton::singleton() { assert(!ptr); ptr = this; } singleton::~singleton() { assert(ptr == this); ptr = nullptr; } singleton& singleton::get() { assert(ptr); return *ptr; } 1I made up this name myself maybe it will catch ;) 2Anonymous 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, WinAPI, 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 CppCon 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. C++Now 2017: Peter Bindels "Mocking C++" https://github.com/dascandy/hippomocks CppCon 2016: Atila Neves Using C++14 to mock C functions" https://github.com/atilaneves/premock CppCon 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 CppCon 2015: Matt Hargett Advanced Unit Testing in C & C++ https://github.com/cgreen-devs/cgreen Google Test (+ Mock) https://github.com/google/googletest Stop Mocking, Start Testing Mocks & stubs by Ken Scambler Mocks Aren't Stubs 2. 3. 4. 5. 6. 7. 8. 9.
Image: Beyonc - Single Ladies (Put a Ring on It) Idea: C++Now 2017: Peter Bindels "Mocking C++"