dfe*_*aro 7 tdd unit-testing mocking
我有一个带有Web前端,WCF Windows服务后端的.NET应用程序.应用程序非常简单 - 需要一些用户输入,将其发送到服务.该服务执行此操作 - 获取输入(Excel电子表格),提取数据项,检查SQL DB以确保项目尚不存在 - 如果它们不存在,我们向第三方数据供应商发出实时请求并检索结果,将它们插入数据库.它沿途做了一些记录.
我有一个Job类,有一个公共ctor和公共Run()方法.ctor接受所有参数,Run()方法执行上述所有逻辑.每个逻辑功能部分被拆分为一个单独的类 - IParser执行文件解析,IConnection执行与数据供应商的交互,IDataAccess执行数据访问等.Job类具有这些接口的私有实例,并使用DI构建默认情况下实际实现,但允许类用户注入任何接口.
在实际代码中,我使用默认的ctor.在我对Run()方法的单元测试中,我使用通过NMock2.0创建的所有模拟对象.此Run()方法本质上是此应用程序的"顶级"功能.
现在这是我的问题/问题:这个Run()方法的单元测试很疯狂.我有三个模拟对象,我发送到ctor,每个模拟对象设置自己的期望.最后,我验证.我有一些Run方法可以采用的不同流程,每个流程都有自己的测试 - 它可以找到数据库中的所有内容,而不是向供应商发出请求......或者可以抛出异常并且作业状态可以被设置为'失败'...或者我们可以得到我们没有数据并且需要发出供应商请求的情况(因此需要进行所有这些函数调用).
现在 - 在你对我大喊并说'你的Run()方法过于复杂之前!' - 这个Run方法只有50行代码!(它确实调用了一些私有函数;但整个类只有160行).由于所有"真实"逻辑都是在此类声明的接口中完成的.但是,这个函数最大的单元测试是80行代码,有13个调用Expect.BLAH().. _
这使得重新考虑了巨大的痛苦.如果我想改变这个Run()方法,我必须编辑我的三个单元测试并添加/删除/更新Expect()调用.当我需要重构时,我最终花费更多时间创建模拟调用,而不是实际编写新代码.在这个功能上做真正的TDD使得它变得更加困难,如果不是不可能的话.它让我觉得它根本不值得对这个顶级函数进行单元测试,因为实际上这个类没有做太多逻辑,它只是将数据传递给它的复合对象(它们都是完全单元测试的,不需要嘲讽).
那么 - 我是否应该费心去测试这个高级功能呢?这样做我获得了什么?或者我在这里完全滥用mock/stub对象?也许我应该废弃这个类的单元测试,而只是进行自动化集成测试,它使用对象的实际实现和Asserts()对SQL查询来确保存在正确的最终状态数据?我在这里错过了什么?
编辑:这是代码 - 第一个函数是实际的Run()方法 - 然后我的五个测试测试所有五个可能的代码路径.我因为NDA原因改变了一些,但一般的概念仍然存在.你对我如何测试这个功能有什么看错了,有什么改变的建议让它变得更好?谢谢.
我想我的建议与这里发布的大部分内容相呼应。
听起来好像您的 Run 方法需要进一步分解。如果它的设计迫使您进行比实际更复杂的测试,则说明有问题。请记住,我们正在讨论的是 TDD,因此您的测试应该决定例程的设计。如果这意味着测试私有函数,那就这样吧。任何技术哲学或方法都不应该过于僵化,以至于你无法做感觉正确的事情。
此外,我同意其他一些海报,即您的测试应该分解为更小的部分。问问自己,如果您第一次编写这个应用程序并且您的 Run 函数还不存在,您的测试会是什么样子?该答复可能不是您当前的答复(否则您不会问这个问题)。:)
您确实拥有的一个好处是类中没有太多代码,因此重构它应该不会很痛苦。
编辑
刚刚看到您发布了代码并有一些想法(没有特定的顺序)。
如果这是我的代码,我的目标是让代码到达 Run 中的所有单独过程调用都单独测试的位置,并且 Run 测试只是测试最终结果。给定输入 A、B、C:期望结果 X。给定输入 E、F、G:期望 Y。Run 如何到达 X 或 Y 的细节已经在其他过程的测试中进行了测试。
这些只是我最初的想法。我确信人们可以采取多种不同的方法。
| 归档时间: |
|
| 查看次数: |
1099 次 |
| 最近记录: |