Performance vs. Testability and semi-virtual method calls
I won't try to sit here and say that the issue of performance vs. testability can be summed up in one blog post (or a full article for that matter). However, it's an important one and during my consulting gigs I've come across some interesting questions and answers regarding this area. Here are some of my thoughts on this.
In languages like C++ developing real-time performing applications may also mean having coding-call standards such as "do not use virtual functions" in the feat that a virtual call repeated enough times may slow the system substantially. Unfortunately, avoiding virtual calls takes away a lot of the testability aspect of code, especially when dealing with legacy code, or real time code. The problem is that for your tests to be able to instantiate specific classes or simulate specific situations, a virtual call may save the day by allowing you to perform what's commonly known as "Inherit & Override" whereby you'd create a class that *inherits* from your class under test and then overrides specific methods which may control various inputs into the class or solve timing and other dependency issues. Taking a simple MailSender class as an example, you might want to override a MailSender's "SendDataToExchangeServer" virtual method and implement it as an empty method instead of making all the way through to a real exchange server.
This in turn allows your tests to use the *inherited* class and avoid any unneeded or timely calls to real servers. They can then simulate various problems with the server such as throwing an exception in the overridden call and see what happens in the class under test. The concept is not a very complicated one but you may need a few readings in order to get it (I suggest you try it with one of your own classes you'd like to test).
In any case, this simple method of removing a dependency is impossible to implement without a virtual method (not an an easy enough manner, anyway). What should you do when faced with the problem of conflicting constraints-- you'd like to test the class but testing it might make it less performing:
- "Premature optimization is the root of all evil": Consider making the method virtual anyway. In fact, one of the first things you learn in a software optimization course is this: "Make it work, then optimize it". Same thing here. Make the software work, and make sure you know it works with tests. When it does work, if you still need to optimize it, you'll have what I call as "known optimization points" - profile your code and see what part of code is the most problematic perf wise. If it's due to a virtual method that could have been non virtual, you can remove that "virtual" part and you've just optimized your application. Of course, that part is now no longer testable.
- Optimize early - lose testability, That's the worst way to go.
- Combine the two approaches: use what I fondly call "Semi-Virtual method calls". What are those? well, they're methods which, when compiled with tests, act as virtual functions, and when compiled without tests act as non virtual functions. The easiest way to do this is to simply write the "virtual" part on a different line and surround it with "#ifdef"s or macros like so:
public
#ifdef(UNIT_TEST) virtual #endif
void domeSomethingFast(int num){...}
You can apply this approach in many different syntaxes, but the main thing is that the tests get what they need and your code gets what it needs. Notice however, that you're taking the "premature optimization" approach here for your Production code anyway. You're testing its logic, yes. but you're still optimizing really early.