使用UI执行业务逻辑的数据库应用程序单元测试

35

我一个人管理着一个相当大的应用程序(50k+行代码),它管理一些非常关键的业务操作。简单地描述这个程序,我会说它是一个带有从数据库中显示和更改数据能力的花哨用户界面,并且它管理着约1,000个租赁单位、约3,000名租户以及所有财务。

因为代码基础如此庞大,所以当我进行更改时,有时会在其他地方破坏某些东西。通常情况下,我通过在功能级别上检查更改的事项来测试它(即运行程序并通过用户界面操作),但我无法测试每种情况。这就是为什么我想开始使用单元测试的原因。

然而,这不是一个真正的三层程序,其中包括数据库层、业务层和用户界面层。许多业务逻辑是在用户界面类中执行的,并且许多事情都是在事件上完成的。而且,所有的操作都是由数据库驱动的,并且我还没有看到(到目前为止)有关如何对数据库交互进行单元测试的好建议。

对于这个应用程序,如何开始使用单元测试呢?要记住,我以前从未进行过单元测试或TDD。我应该重写它以删除UI类中的业务逻辑(需要大量工作)吗?还是有更好的方法?


1
@Malfist:50K+行代码并不算大,最多也只能算中等规模。我几乎是独立编写了一款200KLOC+的应用程序(不包括测试代码行),但我认为它只是中等规模,不算大 :) - SyntaxT3rr0r
11个回答

16

我建议首先使用一些工具通过UI来测试应用程序。有许多工具可以用来创建测试脚本,模拟用户通过应用程序进行点击。

我还建议您在添加新功能的同时开始添加单元测试。一旦应用程序开发完成后再创建完整的覆盖范围是非常费时的,但如果您逐步地进行,那么您将分配努力。

我们通过使用专门用于单元测试的单独数据库来测试数据库交互。通过这种方式,我们有一个静态和可控的数据集,以便可以保证请求和响应。然后我们创建C#代码来模拟各种场景。我们使用nUnit来做这件事。


2
随着添加功能,为单元测试加上 +1,但我认为在添加新功能时,应该机会主义地重构旧的未经测试的代码,并在进行重构时为其添加新的测试。这也可以作为“入口点”,逐步增加测试并增加测试覆盖率。 - Pete

9

6

有一个选项是这样的--每当出现一个错误时,编写一个测试来帮助您找到错误并解决问题。确保测试在修复错误后通过。然后,一旦错误被解决,您就拥有了一个工具,可以帮助您检测未来可能影响您刚刚修复的代码块的更改。随着时间的推移,您的测试覆盖率将提高,并且您可以在进行可能产生深远影响的更改时随时运行不断增长的测试套件。


这总是一个好主意,但这并不能帮助测试可能出乎意料的奇怪交互。 - Ben S

6
TDD意味着在开发过程中构建(并运行)单元测试。如果您想要做的是事后添加单元测试,可以考虑使用类似Typemock(一种商业产品)的工具。此外,您可能已经构建了一个不适合进行单元测试的系统,在这种情况下,需要进行一些(或大量)重构。

5
首先,我建议阅读一本关于单元测试的好书,比如The Art Of Unit Testing。在您的情况下,现有代码进行测试驱动开发已经有些晚了,但如果您想围绕它编写单元测试,那么我建议您采取以下步骤:
  1. 将要测试的代码隔离到代码库中(如果它们还不在库中)。
  2. 列出最常见的用例场景,并将其转换为使用您的代码库的应用程序。
  3. 确保您的测试程序按照您的期望工作。
  4. 使用测试框架将您的测试程序转换为单元测试。
  5. 获得绿灯。如果没有,则您的单元测试存在问题(假设您的代码库正常工作),您需要进行一些调试。
  6. 增加单元测试的代码和场景覆盖范围:如果输入意外结果会怎样?
  7. 再次获得绿灯。如果单元测试失败,则很可能是您的代码库不支持扩展场景覆盖,所以需要重构!
对于新代码,我建议您尝试使用测试驱动开发。
祝您好运(您会需要的!)

5
我建议阅读Michael Feathers的书籍《与遗留代码高效工作》。这本书将向您展示许多技术,逐步增加代码库中的测试覆盖率(并在此过程中改善设计)。请查看Working Effectively with Legacy Code

3

重构是更好的方法。虽然这个过程比较艰巨,但你一定要将表示层和业务逻辑分开。除非你进行了这种分离,否则你将无法对你的业务逻辑编写良好的单元测试。就是这么简单。

在重构过程中,你很可能会发现你甚至不知道存在的错误,并且最终会成为一个更好的程序员!

此外,一旦您重构了代码,您将注意到测试数据库交互变得更加容易。您将能够编写执行操作(例如“添加新租户”)的测试,该操作涉及创建一个模拟租户对象并将“他”保存到数据库中。对于下一个测试,您将编写“GetTenant”,尝试从数据库中获取刚刚创建的那个租户并将其加载到内存中...然后比较第一个和第二个租户以确保所有字段匹配值。等等。


0

我认为将业务逻辑与用户界面分离始终是一个好主意。其中包括更容易的单元测试和可扩展性等多个好处。你可能也想要参考基于模式的编程。这里有一个链接 http://en.wikipedia.org/wiki/Design_pattern_(computer_science) 可以帮助你了解设计模式。

现在你可以做的一件事情是,将所有业务逻辑和不同的业务基础功能隔离在你的用户界面类中,然后在每个用户界面构造函数或页面加载中进行单元测试调用以测试每个业务功能。为了提高可读性,你可以在业务功能周围应用 #region 标记。

为了长期受益,你应该学习设计模式。选择适合你项目需求的模式,并使用设计模式重新设计你的项目。


一个设计模式不足以适用于这么大的应用程序,而且在不需要的地方强行使用设计模式被认为是反模式。这是初学者在学习时会犯的错误。在需要的地方使用设计模式是这个应用程序中所采用的做法。 - Malfist

0

这取决于您使用的编程语言。但通常情况下,从一个简单的测试类开始,使用一些虚构的数据(但仍然是一些“真实”的数据)来测试您的代码。让它模拟应用程序中会发生的情况。如果您要更改应用程序的特定部分,请在更改代码之前编写可工作的代码。现在,由于您已经编写了代码,因此在尝试测试整个应用程序时,启动测试将是一个相当大的挑战。我建议从小处开始。但是,现在您编写代码时,请先编写单元测试,然后再编写代码。您还可以考虑重构,但我会权衡重构与边进行单元测试时逐步重写的成本。


0

没有比尝试单元测试更好的入门方式了 - 它不需要很长时间,而且有趣且让人上瘾。但前提是你正在处理可测试的代码。

然而,如果你试图通过一次性修复像你描述的应用程序来学习单元测试,你可能会感到沮丧和泄气 - 而且很有可能你会认为单元测试是浪费时间。

我建议下载一个单元测试框架,例如NUnitXUnit.Net。这些框架中的大多数都有在线文档,提供简要介绍,例如NUnit Quick Start。阅读它,然后选择一个简单的、自包含的类,该类具有以下特点:

  • 几乎没有或没有依赖于其他类 - 至少不依赖于复杂类。
  • 具有某些行为:一个简单的容器带有一堆属性并不能真正向你展示关于单元测试的很多内容。

尝试编写一些测试以获得该类的良好覆盖率,然后编译和运行这些测试。

一旦掌握了这个技巧,开始寻找 重构现有代码 的机会,特别是在添加新功能或修复错误时。当这些重构导致满足上述条件的类时,请为它们编写一些测试。习惯后,可以通过编写测试来开始


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