如何对非公开逻辑进行单元测试

3
在某些情况下,单元测试可能非常困难。通常人们建议只测试公共API,但在某些情况下这是不可能的。如果您的公共API依赖于文件或数据库,则无法进行正确的单元测试。那么你该怎么办呢?
由于我第一次进行TDD,我正在尝试找到适合我的单元测试风格,因为似乎没有一种万能的方法。我发现了两种方法来解决这个问题,但它们都存在缺陷。一方面,您可以尝试获取程序集并测试内部功能。另一方面,您可以实现接口(仅用于单元测试),并在单元测试中创建虚假对象。这种方法一开始看起来很好,但随着尝试使用这些虚拟对象传输数据时变得越来越丑陋。
有没有“好”的解决方法?其中哪一个更少有缺陷?或者还有第三种方法吗?

3个回答

3
我在TDD中尝试了几次,但一直困扰于同样的问题。对我来说,突破口是当我意识到我的导师所说的意思:“我们不想测试框架。”(在我们的例子中,这是.Net框架)。
在你的情况下,听起来好像是有一些与文件和数据库接口的业务逻辑。我所做的是尽可能地将文件和数据库逻辑抽象成最薄的层。然后,您可以使用Mock(fakes或stub)模拟文件和数据库层。这将使您能够测试场景,例如:如果我的数据库返回这种类型的信息,我的业务逻辑是否正确处理它?同样,对于文件访问,您可以测试找出要打开哪个路径中的文件的代码,并且可以测试您的逻辑是否能够正确拆分任何给定文件的内容并正确使用它。
例如,如果您的文件访问层由一个函数组成,该函数接受路径名和文件名并返回文件内容的长字符串,则您不需要真正测试它,因为基本上您正在对框架/操作系统进行单个调用,那里没有太多错误。
目前,我正在开发一个将我们的数据库包装为一堆返回POCO列表的函数的系统。业务层易于理解,并且可以通过模拟轻松实现。
这种方式需要一些时间才能适应,但是一旦你理解了它,它绝对很美妙。
最后,从您的问题中,我猜想您正在处理旧代码并尝试为新组件执行TDD。这比在全新的开发上执行TDD要困难得多。如果可能的话,请尝试在新系统(或良好隔离的系统)上进行第一次TDD尝试。一旦您学会了机制,将部分TDD引入遗留系统会变得更加容易。

2
If your public API depends on files or databases you can't unit test properly. So what do you do?

有一个可以使用的抽象层级。
  • IFileSystem / IFileStorage(用于文件)
  • IRepository / IDataStorage(用于数据库)
由于这个层级非常薄,所以它的集成测试将很容易编写和维护。所有其他代码都将支持单元测试,因为很容易模拟与文件系统和数据库的交互。
On the one hand, you could try to friend your assemblies and test the features that are internal. 

人们在类违反了单一职责原则(SRP)并且没有使用依赖注入(DI)时会遇到这个问题。
有一个很好的规则是,只有通过它们的公共方法/属性才能测试类。如果内部方法被其他人使用,则可以接受对它们进行测试。私有或受保护的方法不应该因为测试而变成内部方法。
On the other hand, you could implement interfaces (only for the purpose of unit testing) and create fake objects within your unit tests.  

是的,由于模拟框架的限制,接口易于进行模拟。如果您可以创建一个类型的实例(假/存根),则您的依赖项不应实现接口。
有时人们为其领域实体使用接口,但我不支持它们。
为了简化与伪造工作的操作,使用了两种模式:
1. Object Mother 2. Test Data Builder 当我开始编写单元测试时,我从“Object Mother”开始。现在我正在使用“Test Data Builder”。
在Michael Feathers的书Working Effectively with Legacy Code中有很多好的想法可以帮助您。

1
不要让难以处理的事情阻碍你...如果由于数据库或文件集成而本质上难以测试,那么暂时忽略它。很可能您可以使用依赖注入等方法将难以测试的内容重构为更易于测试的内容,并测试易于测试的内容,建立良好的单元测试套件...当您对难以测试的内容进行重构时,您将拥有更高的置信区间,即它不会破坏其他任何东西...并且为了使某些东西更容易测试而进行重构是一种很好的重构理由...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接