在 C# 中自动测试"untestable"应用程序。单元/集成/功能/系统测试 - 如何?



我很难将自动化测试引入到特定的应用程序中。我找到的大部分材料都是关于单元测试的。这对这个应用程序没有帮助,原因如下:

  • 应用程序是多层的
  • 写的时候没有考虑到测试(很多单例保持应用级别设置,对象不可模拟,很多地方需要"状态")
  • 应用程序非常以数据为中心
  • 大多数"功能进程"是端到端(客户端->服务器->数据库)

考虑这个场景:

var item = server.ReadItem(100);
var item2 = server.ReadItem(200);
client.SomethingInteresting(item1, item2);
server.SomethingInteresting(item1, item2);

服务器调用的定义:

Server::SomethingInteresting(Item item1, Item item2)
{
  // Semi-interesting things before going to the server.
  // Possibly stored procedure call doing some calculation
  database.SomethingInteresting(item1, item2);
}

如何设置一些自动化测试,其中有客户端交互,服务器交互,然后数据库交互?

我明白这并不构成一个"单位"。这可能是一个功能测试的例子。

以下是我的问题:

  • 像这样测试应用程序是失败的吗?
  • 这可以完成使用像nUnit或MS测试?
  • 这个测试的"设置"是否只是强制一个完整的应用程序启动和登录?
  • 我是否可以简单地从数据库中重新读取item1和item2来验证它们是否被正确写入?
  • 你们有什么关于如何开始这个艰巨的过程的提示吗?

任何帮助都将非常感激。

如果这是一个非常重要的应用程序,预计将持续几年,那么我建议从小处开始。开始寻找你可以测试的小片段,或者通过对代码的小修改进行测试,使其可测试。

这是不理想的,但在一年左右的时间里,你可能会比今天好得多。

您应该查看集成测试运行器,如Cucumber或fitness。根据您的场景,集成测试运行器将充当服务器的客户机并对服务器进行适当的调用,或者它将与您现有的客户机共享一些域代码并运行该代码。您将为您的场景提供应该进行的调用序列,并验证输出的结果是否正确。

我认为您至少可以使您的应用程序的某些部分可测试的一点工作。好运!

看看Pex和mole。Pex可以分析你的代码并生成单元测试来测试所有的边界条件等等。您可以开始引入一个mock框架来开始去掉一些复杂的位。

更进一步,您必须开始使用集成测试来访问数据库。要做到这一点,您必须控制数据库中的数据并在测试完成后进行清理。有几种方法。您可以运行sql脚本作为每个测试的设置/删除的一部分。只需在属性中指定sql脚本,就可以使用诸如Postsharp之类的编译时AOP框架来运行sql脚本。或者,如果你的项目不想使用Postsharp,你可以使用内置的dot net特性"上下文绑定对象"来注入代码,以AOP的方式运行Sql脚本。更多细节在这里- http://www.chaitanyaonline.net/2011/09/25/improving-integration-tests-in-net-by-using-attributes-to-execute-sql-scripts/

基本上,我建议的步骤是,

1)安装Pex并生成一些单元测试。这将是生成一组好的回归测试的最快、最省力的方法。

2)分析单元测试,看看是否有其他测试或测试的排列可以使用。如果有的话,用手写。如果需要,可以引入一个mock框架。

3)开始进行端到端集成测试。编写sql脚本,在数据库中插入和删除数据。编写将运行插入sql脚本的单元测试。在您的应用程序前端调用一些非常高级的方法,例如"AddNewUser",并确保它一直到后端。前端高级方法可以调用各个层中任意数量的中间方法。

4)一旦你发现自己用这种模式写了很多集成测试"运行sql脚本插入测试数据"->调用高级函数方法->"运行sql脚本清理测试数据",你就可以清理代码并使用AOP技术(如Postsharp或上下文绑定方法)封装这个模式。

理想情况下,您可以随着时间的推移重构代码,使其更易于测试。理想情况下,这方面的任何增量改进都将允许您编写更多的单元测试,这将极大地增加您对现有代码的信心,以及在不破坏现有测试代码的情况下编写新代码的能力。

我发现这样的重构通常也会带来其他好处,比如更灵活和可维护的设计。在尝试证明工作的合理性时,最好记住这些好处。

像这样测试应用程序是注定要失败的吗?

我做自动化集成测试已经很多年了,发现这对我自己和我工作的公司来说都是非常有益的:)我最近才开始理解如何让一个应用程序在过去的3年左右进行完全的单元测试,在那之前我一直在做完整的集成测试(使用自定义实现/hack测试钩子)。

所以,不,这不是一个失败的原因,即使应用程序的架构是这样的。但是如果你知道如何进行单元测试,你可以从重构应用程序中获得很多好处:测试的稳定性和可维护性,编写测试的便利性,以及将失败隔离到导致失败的特定代码区域。

这可以完成使用像nUnit或MS测试吗?

是的。你可以从。net单元测试库中使用网页/UI测试框架,包括Visual Studio内置的一个。

通过直接调用服务器而不是通过UI,您也可能获得很多好处(类似于重构应用程序所获得的好处)。您还可以尝试模拟服务器,并单独测试客户端应用程序的GUI和业务逻辑。

这个测试的"设置"是否只是强制一个完整的应用程序启动和登录?

在客户端,是的。除非你想测试登录,当然:)

在服务器端,您可以让服务器继续运行,或者在测试之间执行某种DB恢复。DB恢复是痛苦的(如果你写错了,会很不稳定),并且会减慢测试速度,但是它会极大地帮助测试隔离。

我是否可以简单地从数据库中重新读取item1和item2来验证它们是否被正确写入?

典型的集成测试基于有限状态机的概念。这样的测试将被测代码视为一组状态转换,并对系统的最终状态做出断言。

因此,您可以事先将DB设置为已知状态,然后在服务器上调用一个方法,然后检查DB。

您应该始终在低于您正在执行的代码级别的级别上执行基于状态的断言。因此,如果要执行web服务代码,请验证数据库。如果您运行客户端并运行服务的真实版本,请在服务级别或DB级别进行验证。永远不要在运行web服务的同时对同一个web服务执行断言(例如)。这样做掩盖了bug,并且不能给您真正的信心,您实际上正在测试所有组件的完整集成。

对于如何开始这个令人生畏的过程,你们有什么建议吗?

把你的工作分开。识别系统的所有组件(每个组件,每个运行的服务等)。试着把它们分成自然的类别。花些时间为每个类别设计测试用例。

设计测试用例时要考虑优先级。检查每个测试用例。考虑一个可能导致它失败的假设场景,并尝试预测它是否会成为一个停止错误,如果错误可以搁置一段时间,或者如果它可以被推到另一个版本。根据这些猜测为您的测试用例分配优先级。

确定您的应用程序的一部分(可能是一个特定的功能),它具有尽可能少的依赖关系,并且只为它编写最高优先级的测试用例(理想情况下,它们应该是最简单/最基本的测试)。完成后,转到应用程序的下一个部分。

一旦你有了应用程序的所有逻辑部分(所有的程序集,所有的特性)被你的最高优先级的测试用例所覆盖,尽你所能让那些测试用例每天运行。在进行额外的测试实现之前,修复这些情况下的测试失败。

然后获得在测试应用程序上运行的代码覆盖率。查看您错过了应用程序的哪些部分。

对测试用例的每个连续的高优先级集重复,直到整个应用程序在某个级别上被测试,并且您发布:)

最新更新