我为什么要进行unit testing和Web测试(而不仅仅是网络测试)?

我目前的立场是:如果我使用Web测试(在我的情况下通过VS.NET’08测试工具和WatiN,可能)使用代码覆盖率和广泛的数据彻底测试我的ASP.NET应用程序,我应该没有需要编写单独的unit testing,因为我的代码将通过所有层与UI一起进行测试。 代码覆盖将确保我能够访问每个function代码(或显示未使用的代码),并且我可以提供涵盖所有合理预期条件的数据。

但是,如果您有不同的意见,我想知道:

  1. unit testing给出的额外好处是certificate将其包含在项目中的努力是合理的(请记住,我正在进行Web测试,因此在许多情况下,unit testing将涵盖Web测试已经涵盖的代码)。

  2. 你能用详细的例子详细解释你的理由吗? 我经常看到诸如“这不是它的意义”或“它促进更高质量”的回应 – 这实际上并没有解决我必须面对的实际问题,也就是说,我怎么能certificate – 有实际结果 – 支出更多时间测试?

代码覆盖将确保我能够访问每个function代码

“命中” 并不意味着“测试”

仅进行Web测试的问题在于它只能确保您点击代码,并且它在高级别上看起来是正确的。

只是因为你加载了页面,并没有崩溃,并不意味着它实际上正常工作。 以下是我遇到的一些事情,’网络测试’覆盖了100%的代码,但完全错过了一些非常严重的错误,unit testing不会有。

  1. 页面从缓存中正确加载,但实际数据库已损坏
  2. 页面加载了数据库中的每一个项目,但只显示了第一个 – 它看起来很好,即使它完全失败,因为它花了太长时间
  3. 该页面显示一个看起来有效的数字,实际上是错误的,但它没有被拿起,因为1000000很容易被误认为100000
  4. 页面按重合显示有效数字 – 10×50与25×20相同,但其中一个是错误的
  5. 该页面应该向数据库添加一个日志条目,但这对用户不可见,因此没有看到。
  6. 绕过身份validation使网络测试真正起作用,因此我们错过了身份validation代码中的一个明显错误。

很容易想出更多这样的事情的例子。

你需要两个unit testing来确保你的代码实际上做了它应该在低级别做的事情,然后function/集成(你称之为web)测试在那些之上,以certificate它实际上工作时所有那些经过unit testing的小件都被链接在一起。

unit testing在转向时可能比网络测试快得多。 这不仅在开发时间方面是正确的(如果你可以直接测试一个方法,那么你可以直接测试一个方法,而不是你必须构建一个特定的查询,最终会在几个层次之后点击它),而且在执行时间(按顺序执行数千个请求将比执行短方法花费数千次)。

unit testing将测试小function单元,因此当它们失败时,您应该能够很容易地找出问题所在。

此外,使用unit testing模拟依赖关系要比完全前端更容易 – 除非我错过了一些非常酷的东西。 (几年前,我研究了模拟和网络测试如何整合,当时没有什么合适的。)

unit testing通常不能certificate任何给定的function集合起作用 – 至少它不应该。 它certificate您的课程合同按预期运作。

验收测试更倾向于客户要求。 每个要求都应该有验收测试,但验收测试和unit testing之间确实没有要求 – 它们甚至可能不在同一个框架中。

unit testing可用于驱动代码开发,重新测试的速度是一个重要因素。 在测试时,您经常会删除被测试的类所依赖的部件以进行单独测试。

验收测试系统就像你提供它一样 – 从GUI到数据库。 有时它们需要数小时或数天(或数周)才能运行。

如果你开始认为它是两个完全不同的野兽,你将成为一个更有效的测试者。

良好,专注的unit testing可以在发现问题时更快地找到并解决问题。 当一个写得很好的unit testing中断时,你几乎知道失败的意义和导致它的原因。

此外,它们通常运行速度更快,这意味着您在开发期间更有可能在编辑 – 编译 – 测试周期中运行它们(而不是仅在您即将签到时)。

编写unit testing时,您将被迫以更好的方式编写代码。 更松散耦合,更面向对象。 这将带来更好的架构和更灵活的系统。

如果你以TDD风格编写unit testing,你可能不会做那么多不必要的代码,因为你将专注于微小的步骤,只做必要的。

在进行重构以改进代码以提高可维护性并减少代码味道时,您将更有信心。

unit testing本身将作为系统执行和不执行的操作的安静文档。

这些只是我在将TDD应用到我的工作时注意到的一些好处。

还有一个方面 – 原谅这个位于的理想环境:

假设您有3个组件最终必须协同工作。 每个都可以通过5个unit testing单独进行unit testing (无论这意味着什么)。 这使得5 + 5 + 5 = 15unit testing完全覆盖。

现在,如果你有集成/网络/组合测试,一起测试所有组件,你需要(记住这个抽象场景的理想世界) 5 * 5 * 5 = 125测试,测试所有排列,给你相同的结果上面的15个测试用例(假设您甚至可以触发所有排列,否则会出现未经测试/未指定的行为,以后在扩展组件时可能会咬你)。

此外,如果function发生变化,125个测试用例将具有更复杂的设置,更长的周转时间和大大降低的可维护性。 我宁愿选择15个unit testing加上一些基本的网络测试,以确保组件的接线方式正确。

unit testing可以提供速度,最重要的是,引入故障或错误的位置精确度。 它使您能够测试每个组件,与每个其他组件隔离,并确保它按预期工作。

例如,如果您进行了Web测试,则会将Ajax请求发送回服务器上的服务,该服务随后会访问数据库,然后数据库将失败。 它是导致问题的Javascript,服务,业务逻辑或数据库吗?

如果你自己单独测试每个服务,存根/模拟数据库或每个业务逻辑单元,那么你更有可能确切地知道错误的位置。 unit testing不仅涉及覆盖范围(尽管很重要),更多的是关于隔离。

就像Dijkstra所说的那样:单​​元测试只能用来表明软件有缺陷,而不是certificate它没有缺陷。 因此,一般来说,每次访问每个代码路径(并获得100%覆盖率)与测试无关 – 它只是有助于消除bitrot。

如果您正在阅读本书,那么只有在编写了触发该错误的unit testing后,才能消除每个严重错误。 巧合的是,修复错误意味着这个特定的unit testing不再失败。 将来,此unit testing会检查此特定错误是否仍然存在。

编写一个触发特定错误的unit testing要比写一个端到端(web)测试要容易得多,这个测试只能执行并且不会运行大量完全不相关的代码(也可能会失败并且混乱根本原因分析)。

unit testing测试每个组件是否有效。 这些非常有助于发现接近创建时间的缺陷,从而大大降低修复缺陷的成本,并显着减少最终发布的缺陷数量。 此外,良好的unit testing使重构更容易,更强大。

集成测试(或在这种情况下为“web”测试)也非常重要,但是比unit testing更具防御性。 单个集成测试涵盖了如此大量的代码,当一个代码失败时,需要进行大量调查以确定导致失败的缺陷(或可能是缺陷组)。 这样做成本很高,尤其是当您尝试测试发布版本以将其发布时。 这是更加昂贵的,因为引入修复程序的错误的机会往往相当高,并且失败的可能性阻碍了对发布的进一步测试(这对开发周期来说是非常昂贵的)。

相反,当unit testing失败时,您确切地知道缺陷代码的位置,并且您通常确切地知道代码的错误。 此外,unit testing失败应该一次只影响一个开发人员,并在签入代码之前修复。

修复错误总是比以前更昂贵。 总是。

考虑建造一辆汽车。 您是否会等到整个车辆从assembly线上下来测试每个组件是否有效? 此时,如果您发现CD播放器或引擎或空调或巡航控制不起作用,您必须让整个车辆离线,解决问题,然后重新测试一切(希望不会揭示任何新的缺陷)。 这显然是错误的做法,就像尝试发布软件显然是错误的,而只是测试它是否在流程结束时工作而不是从头开始沿着每一个重要步骤。

这取决于ASP.NET应用程序架构如何。 如果网页仅仅是连接底层业务逻辑层或数据访问层,那么独立于ASP.NET状态模型工作的unit testing对于开发人员和运行来说比类似的WaitiN测试更快。

我最近在Visual Studio 2003中开发了一个遗留ASP.NET应用程序的区域,其中NUnit作为测试框架。 以前的测试涉及通过UI测试来确保function正确实现,而90%的测试都是在不需要UI交互的情况下进行的。

我唯一的问题是时间估计 – 其中一项任务是在Trac中计划为数据访问/业务逻辑需要1天,UI创建和测试需要2天。 随着NUnit在数据访问/业务逻辑上运行,开发部分的时间从1天增加到2天。 用户界面开发时间缩短为1/2天。

这继续将新模块中的其他任务添加到应用程序中。 该unit testing发现的错误更快,并且以一种不那么痛苦的方式(对我而言)并且我对应用程序的运行更有信心。 更好的unit testing是非常可重复的,因为它们不依赖于任何UI重新设计,因此设计中的更改在编译时失败,而不是在运行时,因此往往不那么脆弱。

unit testing允许更严格的性能测试,并且更容易确定瓶颈发生的位置。 对于大型应用程序,当10,000个用户同时执行一个方法时,性能成为一个主要问题,如果该方法需要1/100秒执行,可能是因为编写的数据库查询不好,因为现在一些用户必须等待最多10秒钟才能加载页面。 我知道我个人不会等待那么长时间才能加载页面,而只会转移到另一个地方。

unit testing允许您在添加前端的复杂性之前,专注于获取数据,应用规则和validation业务流程的逻辑。 在编写和执行unit testing时,我知道应用程序的基础结构将支持我在进程之间来回传递的数据类型,我的事务正常工作等。

换句话说,当您开始验收测试时,潜在的故障点会更好地隔离,因为您已经使用unit testing解决了代码中的潜在错误。 根据unit testing的历史,您将知道只有某些模块会抛出某些错误。 这使得追踪罪魁祸首代码变得更加容易。