
Unit Testing Anti-Pattern: Testing Private Methods
Testing
7 Articles
Table of Contents
What we gonna do?
Unit testing private methods is a common dilemma. The general rule is that private methods should not be tested directly. Instead, tests should validate the observable behavior of the system.
Why we gonna do?
Exposing private methods for testing leads to tight coupling between tests and implementation details, reducing their resistance to refactoring. This results in fragile tests that break frequently, even when functionality remains intact. A better approach is to test private methods indirectly through the class's public API.
How we gonna do?
Instead of testing private methods, consider whether there is a missing abstraction . If a private method is too complex, it should likely be extracted into a separate class. Consider the following example:
public class Order { private Customer _customer; private List<Product> _products; public string GenerateDescription() { return $"Customer: {_customer.Name}, Products: {_products.Count}, " + $"Total Price: {GetPrice()}"; } private decimal GetPrice() { decimal basePrice = /* Calculate based on _products */; decimal discounts = /* Calculate based on _customer */; decimal taxes = /* Calculate based on _products */; return basePrice - discounts + taxes; } }
Here, GetPrice() contains important business logic but is private. Instead of exposing it, we extract it into a separate class:
public class Order { private Customer _customer; private List<Product> _products; public string GenerateDescription() { var calc = new PriceCalculator(); return $"Customer: {_customer.Name}, Products: {_products.Count}, " + $"Total Price: {calc.Calculate(_customer, _products)}"; } } public class PriceCalculator { public decimal Calculate(Customer customer, List<Product> products) { decimal basePrice = /* Calculate based on products */; decimal discounts = /* Calculate based on customer */; decimal taxes = /* Calculate based on products */; return basePrice - discounts + taxes; } }
Now, we can test PriceCalculator independently without exposing private methods.
Summary
Testing private methods directly is usually an anti-pattern, as it couples tests to implementation details. Instead, test them indirectly through the public API or extract them into separate classes when necessary. In rare cases where a private method is an integral part of the observable behavior (such as a private constructor used by an ORM), making it public for testing might be acceptable.