将公共方法设为virtual以提升可测试性:是否真有必要?
Great question—this is such a common point of confusion when refining your unit testing workflow, especially with mocking frameworks. Let’s break this down clearly:
First, Why Does Making Methods Virtual Help with Mocking?
Most popular open-source mocking frameworks (like Moq, NSubstitute, or Rhino Mocks) work by creating a derived class from your concrete type at runtime. To replace the behavior of a method in the mock, the framework needs to override that method in the derived class.
In statically typed languages like C#, you can only override methods marked as virtual (or abstract). If a method isn’t virtual, the derived class can’t change its behavior—so the mocking framework has no way to inject your test-specific logic into that method.
Here’s a quick example to illustrate:
Non-virtual method (can’t mock):
public class OrderService { public decimal CalculateTotal(Order order) { // Real logic you want to mock in tests return order.Items.Sum(i => i.Price); } }
If you try to mock CalculateTotal with Moq, you’ll get an error—because the method isn’t virtual, Moq can’t override it.
Virtual method (can mock):
public class OrderService { public virtual decimal CalculateTotal(Order order) { return order.Items.Sum(i => i.Price); } }
Now Moq can create a derived class that overrides CalculateTotal to return a fixed value for your test.
So, Should You Actually Do This?
It depends on your context:
For legacy code or situations where you can’t refactor to use interfaces: Yes, this is a perfectly reasonable compromise. If you can’t extract an interface from an existing class (maybe due to time constraints, or the class has too many tightly coupled responsibilities), marking the methods you need to mock as
virtuallets you write unit tests without a full rewrite.For new code: No—prefer using interfaces over virtual methods. Interfaces define a clear contract between components, making your code more modular and testable by design. Mocking interfaces is more natural (frameworks don’t need to rely on derived classes), and you avoid cluttering your concrete classes with
virtualmodifiers that exist solely for testing.Don’t overdo it: You don’t need to mark every public method as
virtual—only the ones you actually need to mock in your tests. If a method is a pure function with no external dependencies (like a simple calculation), you probably don’t need to mock it at all.
Alternative to Virtual Methods (If You Can’t Use Interfaces)
Some commercial mocking frameworks (like TypeMock or JustMock) can mock non-virtual methods by using IL weaving or other low-level tricks. But these tools come with a cost, and they can make your tests more tightly coupled to implementation details. For most projects, sticking with open-source frameworks and using virtual when necessary is the pragmatic choice.
How This Fits with Your Other Practices
Your other points (dependency injection, small focused methods, avoiding static/singleton/sealed classes) align perfectly with this. Dependency injection works best with interfaces, which eliminates the need for virtual methods entirely. Small, cohesive methods mean you’ll have fewer methods to mock in the first place—making your tests simpler regardless of whether you use interfaces or virtual methods.
内容的提问来源于stack exchange,提问作者Ezoela Vacca

