Since my beginnings with unit testing, I have been taught that private methods are not testable, SRP requires that we inject all dependecies and so on. Trying to be really "pure", I than had tendency to avoid private methods, create a lot of one-method classes with awkward names used in only one place, and, of course, with an interface for each.
I consider it a very big anti-pattern, therefore started to be more pragmatic, and use some balance between SRP in this radical form and common sense. What's only internally used should stay inside. But what about isolation? I assume that failing unit test should point almost immediately at responsible code. Suposing we have such class:
public class Estimator
{
public double CalculateManHours(List<TaskEstimation> estimations)
{
double sum = 0;
foreach (estim in estimations)
{
double weightedEstim = CalculateWeightedEstimation(estim.Optimistic, estim.Realistic, estim.Pesimistic);
sum+=weigtedEstim;
}
return sum;
}
private double CalculateWeightedEstimation(double optimistic, double realistic, double pesimistic)
{
return (3*pesimistic+2*realistic+optimistic)/3;
}
}
We would like to test both methods, mocking CalculateWeightedEstimation. What can we do? First of all use internal keyword instead of private. But how to mock this internal method? Using next great C# feature - delegates:
public class Estimator
{
internal Func<double, double,double,double> _calculateWeightedEstimationFunc;
internal Func<double, double,double,double> CalculateWeightedEstimationFunc
{
get { return _calculateWeightedEstimationFunc ?? CalculateWeightedEstimation; }
set { _calculateWeightedEstimationFunc = value;}
}
internal double CalculateWeightedEstimation(double optimistic, double realistic, double pesimistic)
{
return (3*pesimistic+2*realistic+optimistic)/3;
}
public double CalculateManHours(List<TaskEstimation> estimations)
{
double sum = 0;
foreach (estim in estimations)
{
double weightedEstim = CalculateWeightedEstimationFunc(estim.Optimistic, estim.Realistic, estim.Pesimistic);
sum+=weigtedEstim;
}
return sum;
}
}
Then we can easily test public method:
public void CalculateManHours_IsSumCorrect()
{
var estimations = new List<TaskEstimation>();
estimations.Add(new TaskEstimation());
estimations.Add(new TaskEstimation());
estimations.Add(new TaskEstimation());
estimations.Add(new TaskEstimation());
var estimator = new Estimator()
{
CalculateWeightedEstimationFunc => (opt,real,pes) => return 1;
}
double result = CalculateManHours(estimations);
double expectedResult = 4;
Assert.AreEqual(expectedResult,result);
}
Is there anything wrong with this approach? Doesn't it break any rules? Of course it mixes two worlds - functional and oo, but I consider it a nice trade-off. Is isolating relatively small methods an overkill? I am curious what do you think about it. Thanks in advance for sharing your opinion.
Aucun commentaire:
Enregistrer un commentaire