There are some design guidelines about testable code in “The Art of Unit Testing”. The first one is “Make methods virtual by default”. I’m curious to know your idea about non-virtual-by-default behavior in C#. I’ve read about Hejlsberg opinions but I think one the most important reasons could be that it may lead us to “composition over inheritance” principal.
Could “composition over inheritance” be one of those reasons which make non-virtual-by-default preferred over virtual-by-default?
UPDATE
Regarding this subject, please consider test-driven point of view; where we want to write testable code. While we are encouraged to make all members virtual by default (at the mentioned book), we can follow “composition over inheritance” and keep going non-virtual-by-default. Isn’t it better?
Explicit virtual is form of Defensive programming. By enforcing what methods can be overriden, you ensure that person who will create a child class cannot create error by doing something that wasn’t intended by parent’s design.
I don’t think it has anything to do with how people approach design of their code from composition vs inheritance point. But there are definitely people who will dismiss it on basis that defensive programming is not good thing.
3
I think it could, yes. Virtual by default would make it a lot easier to override implementations in inherited classes.
Frankly, I think it’s a weird rule. If you really need to override something, then make it virtual. Don’t go making everything virtual just because some guideline tells you to.
It sounds like stuff you’d hear in college (overarching advice that could bite you in the backside when applied without second thought).
I am puzzled as to why a book on testability would be recommending creating virtual methods, unless it is speaking more to a C++ audience than a C# audience.
Making methods virtual should be done only with great care and caution. Most classes I write that are not explicitly abstract are generally sealed
. That way I can be sure that the code that I write is rhobust and thoroughly tested. I can be sure that no other developer is going to inherit my class, violate the Liskov Substitution principle and throw some kind of exception in the code that I have written.
If a developer follows the SOLID principles, particularly the last one (dependency inversion), testability should not be a huge concern. It’s when these principles are violated that testing becomes a lot harder.
Here are my rules of thumb for writing testable code:
- Avoid writing static methods/classes.
- Follow the SOLID principles.
- Seal as many classes as is practical and use the
virtual
keyword very sparingly.
With dependency injection and mocking tools tests are easy to write (even if some become long because of the data that is required to test against).
3