There are many different ways to break dependencies when doing unit testing in C++. We can use approaches described in the book, including constructor injection, setter injection, extract and override, and...yes, the factory design pattern. They work well in some cases so that we should know the pros and cons of every tricks above. However, they have some common shortcomings:
The production code (production_mwc_dep) would do every practical work and it is the default template parameter of mwc. We can use it as following:
Then I do the unit test by writing some mock classes. Here comes the mock part:
Yes!! By using templates, we can really solve the problems we mentioned. However, experienced programmers might ask: how can I create mock classes conveniently just as gmock? The answer is: No, we don't.
Currently I can't find any mock framework which can mock "type". They all mock objects. If we want to mock types and then use the mock types as template parameters(which should be types), we must do it on our own. For now, I would like to try this method for a while to get more experience when using templates as dependency breaking tricks. And might try to create a mock framework to mock type if I can do it. Just wait and see. :-)
- unnecessary run-time indirection by using inheritance (it is getting worse when critical execution path is on the objects)
- unnecessary interfaces should be added and used. (those interfaces are merely for unit test convenience, not so meaningful in production code)
Those traditional tricks used in unit test are based on common OO features. If we focus on C++, can we solve these two problems by different thinking?
I gave template a try by writing a simple word count program. Before we go on, you can clone it to play. The main class (mwc) is a class template with a template parameter contains two dependencies will be used in it.
- /* abstract dependency policy for mwc */
- template<typename FL_, typename INI_>
- struct mwc_dep {
- typedef FL_ FL;
- typedef INI_ INI;
- };
- /* production dependency to be used in mwc */
- typedef mwc_dep<product_file_loader, product_ini_mgr> product_mwc_dep;
- /* mwc, our CUT, have some dependency needed to be broken by abstraction tricks */
- template <typename DEP = product_mwc_dep>
- class mwc {
- typedef typename DEP::FL FL;
- typedef typename DEP::INI INI;
The production code (production_mwc_dep) would do every practical work and it is the default template parameter of mwc. We can use it as following:
- void ProductMWCTester::MWC_QueryValidWord_ReturnCount()
- {
- MC::mwc<> wc;
- wc.load("./data");
- CPPUNIT_ASSERT_EQUAL(2, wc.query("ooooo"));
- }
Then I do the unit test by writing some mock classes. Here comes the mock part:
- class mock_file_loader0: public MC::file_loader {
- public:
- void content(const std::string &fn, std::string &source)
- {
- source = "";
- source += "ooooo\nI\nam who ooooo\nwho ope\nThere\n";
- }
- };
- class mock_ini_mgr0: public MC::ini_mgr {
- public:
- void open(const char *ini_file)
- {
- exclude_words_.insert("The");
- exclude_words_.insert("I");
- exclude_words_.insert("There");
- }
- uint32_t min_word_length() const { return 2; }
- const std::set<std::string> &exclude_words() const { return exclude_words_; }
- bool enable_case_sensitive() const { return true; }
- private:
- std::set<std::string> exclude_words_;
- };
- void MockMWCTester::MWC_QueryValidWord_ReturnCount()
- {
- typedef MC::mwc_dep<mock_file_loader0, mock_ini_mgr0> mock_mwc_dep;
- MC::mwc<mock_mwc_dep> wc;
- wc.load("./data");
- CPPUNIT_ASSERT_EQUAL(2, wc.query("ooooo"));
- }
Yes!! By using templates, we can really solve the problems we mentioned. However, experienced programmers might ask: how can I create mock classes conveniently just as gmock? The answer is: No, we don't.
Currently I can't find any mock framework which can mock "type". They all mock objects. If we want to mock types and then use the mock types as template parameters(which should be types), we must do it on our own. For now, I would like to try this method for a while to get more experience when using templates as dependency breaking tricks. And might try to create a mock framework to mock type if I can do it. Just wait and see. :-)
留言
張貼留言