自从关注DI和TDD之后,我对何时应该创建私有方法感到困惑。你能不能告诉我在制作一个方法私有保持可测试性和依赖注入的时候应该考虑哪些经验法则?
我相信一个例子可能对此有所帮助:
假设我有一个包含3种方法的界面,如下所示:
public interface IWordFrequencyAnalyzer
{
int CalculateHighestFrequency(string forText);
int CalculateFrequencyForWord(string text, string word);
IList<IWordFrequency> CalculateMostFrequentNWords(
string text, int n);
}
现在,我可以编写一个类,它可以实现一个私有方法,它接受一个字符串并可以计算其中单词的频率,然后在每个公共方法中我可以根据它的要求进行操作。在这种情况下我我将能够测试合同。
OR
我可以将私有方法提取到单独的类中,例如WordProcessor,它实现IWordProcessor,使用单个公共方法将句子拆分为单词并将其作为依赖项传递给IWordFrequencyAnalyzer的实现。这样,分割单词的实现也是可测试的。
你会建议采用哪种方法?
谢谢, -Mike
答案 0 :(得分:7)
由于越来越多地使用DI和TDD,我最终越来越少地使用私有方法,但原因并不是因为我需要将它们公开用于测试。更重要的是,作为使用DI的副产品,我正在学习更多关于将SOLID原则应用于我的代码的知识,而这(反过来)正在引导我编写整体方法较少且几乎没有私有的类。
所以,假设您在课程的各种方法中使用了一段代码。你知道DRY,你将它重构为私有方法,一切都很好。除了通常你意识到你可以概括那个私有方法做什么并将该功能作为类的外部依赖注入:这样,你可以测试它并模拟它,当然,但最重要的是你可以重用那个方法甚至在其他课程或其他项目,如果需要。将其移出原始课程是单一责任原则的应用。
正如我所说,我对代码的改变并不直接取决于我使用TDD或DI的事实,但它是TDD鼓励我强制执行的原则的副产品以及DI容器在编写时提供的便利性这种方法产生的许多小班。
编辑:您添加到问题中的示例中的第二种方法是我所谈论的一个很好的例子。您的新WordProcessor类现在可测试的事实是一个优点,但它现在可组合和可重用的事实是真正的好处。答案 1 :(得分:6)
您的方法应为private
,除非您的申请需要public
,而不是您的考试。
一般情况下,您不应仅仅为支持单元测试而public
(internal
可能会有所帮助)。在这种情况下,您通常应该测试public
接口,而不是private
类的详细信息,这些更有可能改变并破坏您的测试。
如果您可以访问副本,Roy Osherove的“The Art Of Unit Testing”很好地解决了这个问题
答案 2 :(得分:2)
当特定方法仅由对象在内部使用时,它们通常从其他方法调用,其中一些方法将是公共的或受保护的。由于遵循DRY(不要重复自己)原则,您更有可能创建私有方法。在这种情况下,您将几个方法使用的一些常用代码提取到这些方法调用的私有方法中。
答案 3 :(得分:1)
将类的每个方法公开并为此类的每个方法编写单元测试将很快造成维护噩梦,因为您必须为生产代码中的每一个小重构更改测试。您希望测试类本身的行为,为此您不需要公开所有方法。
答案 4 :(得分:1)
您应该在可能的情况下始终将您的方法设为私有。不要将它们更改为公开用于单元测试。当你发现很难测试它时,请考虑改变你的类的一点点架构。根据我的经验,通常当我需要测试私有方法时,班级有错误的责任,所以我改变了它。
答案 5 :(得分:0)
单一责任政策将帮助您解决此问题。考虑这个例子:
internal class Boss
{
private bool _notEnoughStaff;
private IList<Employee> _staff;
public Boss(bool notEnoughStaff)
{
_notEnoughStaff = notEnoughStaff;
}
public void GiveOrders()
{
if (_notEnoughStaff)
HireStaff();
foreach (Employee employee in _staff)
{
employee.DoWork();
}
}
private void HireStaff()
{
_staff.Add(new Employee());
}
}
public class Employee
{
public void DoWork()
{
}
}
在这种情况下,我看到的不是一个而是两个责任:老板代表工作和雇用新员工。在这个例子中,我总是将私有方法HireStaff提取到一个新类(让我们称之为HR),然后将它注入到Boss类中。 这是一个非常简单的例子,但随着您在TDD思维方式方面的经验越来越丰富,您会发现没有多少私有方法具有合法性。
此致 的Morten