我有以下代码
public interface IInterpreter
{
decimal Evaluate(string expression);
}
public class Interpreter : IInterpreter
{
public decimal Evaluate(string expression)
{
if (String.IsNullOrWhiteSpace(expression))
throw new ArgumentException("Parameter " + nameof(expression) + " cannot be empty");
var rpnExpression = ConvertToReversePolishNotation(expression);
return EvaluateReversePolishExpression(rpnExpression);
}
...
}
此课程将评估“5 + 5 * 6”或“(3-5)*(2 + 2)+5”等表达式
现在我想编写单元测试。这里唯一的公共函数是Evaluate
,根据所有推荐,只应测试此方法。
问题是我强烈感觉ConvertToReversePolishNotation(expression)
EvaluateReversePolishExpression(rpnExpression)
函数必须用单元测试来覆盖...如果Evaluate
方法的某些单元测试失败,它就不会指向我错误在哪里(在ConvertToRPNExpression
方法或EvaluateReversePolishExpression
函数中)。
所以问题是 - 在这种情况下是否可以为私有函数编写单元测试?
答案 0 :(得分:5)
没有
将ConvertToReversePolishNotation
方法重构为单独的类。它的关注点是转换。
口译员的关注点是评估。如果你'嘲笑'转换的输出应该很容易使用公共Evaluate
方法进行测试。
答案 1 :(得分:2)
我会创建三个类,一个负责验证,一个用于转换,一个用于评估。
比使用构造函数依赖注入从实际实现中解耦。 无论是手动实例化还是委托Container启动它,您都可以更改行为而无需更改解释器。
如果沿着这条路线前进,您可以轻松测试依赖项的调用顺序,因此您将测试Interpreter的行为。 比如,验证,转换,评估。
除了可以测试单个类的每个实现之外。
你的班级也制定单一责任原则,因为负责验证并且还要调用外部类确实执行操作,所以负责描述事件流。
此外,如果您将转换和评估的实现类紧密结合在一起,那么您就违反了Open Close原则。
答案 2 :(得分:1)
是的,没关系。。
您正在进行单元测试Evaluate
,其中包含ConvertToReversePolishNotation
和EvaluateReversePolishExpression
的实际实施。
Aphelion是正确的,你可以将班级分开来自己负责,但只有ConvertToReversePolishNotation
你甚至不需要为EvaluateReversePolishExpression
创建一个私有方法,如果你这样做,因为那已经是它正在做什么(现在)。在调用evaluate之前,调用者应该调用ConvertToReversePolishNotation
的类并将其作为参数传递。
如果您还担心没有单元测试private
方法,那么您可以使用internal
方法。
超出主题:您还必须牢记如何扩展和扩展此功能。如果您的解释器还希望返回decimal
以外的数据类型,会发生什么?你不想也想要最大化泛型吗?
public decimal Evaluate(string expression)
{
if (String.IsNullOrWhiteSpace(expression))
throw new ArgumentException("Parameter " + nameof(expression) + " cannot be empty");
return EvaluateReversePolishExpression(expression);
}