我读到,通常私有方法不需要进行单元测试,因为它们是通过公共方法进行测试的。那么我应该在服务层做什么?
我有一个服务层和一种将文章添加到数据库的方法。此方法检查有关文章及其文件的一些逻辑,然后通过存储库将其保存:
public void AddArticle(ArticleDto article)
{
CheckArticleFiles(article);
CheckArticleTitle(article.Title);
CheckArticleSummary(article.Summary);
CheckArticleBody(article.Body);
_articleRepository.AddArticle(article.MapToDbModel());
_articleRepository.SaveChanges();
}
所有检查代码现在都是公开的,以使它们能够进行测试,我认为这不是正确的方法(由于OOP原则)。另一方面,如果我将它们更改为私有,则应通过 AddArticle 方法对其进行测试。在这种情况下,我认为这不是单元测试。因为它不是一个单元,并且此方法充当协调器。
如果我使用具有私有逻辑的域模型,则会发生这种情况。并且逻辑是需要测试的最重要部分,不需要公开甚至受保护
那我的错误是什么?
答案 0 :(得分:1)
我将围绕每个业务逻辑进行单元测试。
CheckArticleFiles(article);
CheckArticleTitle(article.Title);
CheckArticleSummary(article.Summary);
CheckArticleBody(article.Body);
您没有显示任何代码,但是这些方法似乎具有某些业务规则,这就是您需要测试的内容。
您还希望确保您的存储库可以正常工作,因此您可以进行一些集成测试,这是一些数据,我希望数据库数据看起来像这样...
请注意,Add方法应该返回一些内容,以便您至少可以进行一些完整性检查,例如返回所创建文章的唯一ID,这样您就可以知道您的方法是否有效,并且它使您能够做其他有趣的事情,例如建立指向新创建文章的链接,因为您现在知道了ID。
要添加更多内容是非常困难的,因为到目前为止您尚未实际显示任何有用的代码。例如,我将拥有一个Article类,所有业务逻辑都将进入该类。回购和节省部分不在此范围内。拥有检查标题的公共方法根本不是一件坏事,它使您可以逐个检查事物,而不是将整个事物检查为一件。这取决于您如何使用它。
答案 1 :(得分:1)
通常将接口的某些部分设计为仅在某些上下文中使用(仅在配置期间,仅在初始化期间,仅从某些线程,仅从测试代码,仅在某些系统状态...)。通过编程语言来表达这种信息的可能性非常有限(通常您拥有公共和私有的,经常受到保护,有时更多),并且常常存在克服公共或私有声明的障碍的技巧,例如在使用预处理器技巧的C ++中,在Java和使用自省的类似语言中。例如,在Python中,此类机制已完全由约定代替-如果不遵循约定,迟早会松懈。
从不关注特定编程语言的角度看待事物,因此,我更倾向于将public
,private
等视为代表体系结构约束的逻辑概念。例如,将代表实现细节的一些私有函数作为公共方法提取到一个单独的类中不会改变它们作为实现细节的性质:那么该其他类当然也不应被其他组件使用,并且测试使用该类的代码如果这些实现详细信息发生更改,则不会受到任何影响。我提到这一点只是为了证明,与将方法提取到另一个类并告诉其他人不要使用该类相比,将方法公开给大家并没有多大区别。告诉其他人不要使用该方法。
对于您的情况,您想表示某些功能应被视为实现细节(private
),但仍可出于单元测试的目的而使用。而且,是的,我同意直接测试private
函数比从您称为服务层的间接测试更有意义。当然,这是一个折衷,但是根据我的经验,我也知道这种情况。
最简单的方法是(如果您使用的语言没有像C ++的friend
构造那样提供任何更好的技巧),则应公开这些方法并记录它们不打算使用的方法。如果要使其更加突出,一种可能性是使功能可公开访问,但通过命名来表达使用上的限制。只能由测试使用的功能foo
可以命名为forTestingOnly_foo
。由于在私有上下文中调用该函数时也很麻烦,因此foo
仍然可以是带有非私有包装器forTestingOnly_foo
的私有函数。
如果可以为此类情况建立命名约定,则可以创建一个简单的检查器,以验证没有人(从测试上下文中除外)访问任何这些“可公开访问但逻辑上私有”的方法,并在构建中使用它如果检测到滥用,则会使其失败。是否需要这种方法在很大程度上取决于您所从事的组织文化。