在 C# 中对“不可测试”的应用程序进行自动化测试。单元/集成/功能/系统测试 - 如何?
Posted
技术标签:
【中文标题】在 C# 中对“不可测试”的应用程序进行自动化测试。单元/集成/功能/系统测试 - 如何?【英文标题】:Automated testing for an "untestable" application in C#. Unit/Integration/Functional/System Testing - How To? 【发布时间】:2011-10-20 01:14:44 【问题描述】:我很难将自动化测试引入特定应用程序。我发现的大部分材料都集中在单元测试上。这对这个应用程序没有帮助有几个原因:
应用程序是多层的 编写时没有考虑到测试(许多单例包含应用程序级别设置,对象不可模拟,很多地方都需要“状态”) 应用程序非常以数据为中心 大多数“功能流程”都是端到端的(客户端 -> 服务器 -> 数据库)考虑这种情况:
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 Test 之类的东西来完成吗? 此测试的“设置”是否会简单地强制启动完整的应用程序并登录? 我是否只需从数据库中重新读取 item1 和 item2 以验证它们是否正确写入? 你们对如何开始这个艰巨的过程有什么建议吗?任何帮助将不胜感激。
【问题讨论】:
澄清一下,我理解单元测试各种“SomethingInteresting()”调用的概念。我正在寻找的是一种从头到尾测试过程的方法。 【参考方案1】:如果这是您公司中一个非常重要的应用程序,预计将持续数年,那么我建议从小处着手。开始寻找可以测试的小块,或者对代码进行小的修改以使其可测试。
这并不理想,但在一年左右的时间里,你可能会比现在好得多。
【讨论】:
我知道这在单元测试方面是如何工作的,但是集成测试呢? 虽然有一些工具可以自动对应用程序(Web 或 Windows 应用程序)进行端到端测试,但我对它们的运气并不好。对于一个普通的应用程序来说,设置是如此复杂,以至于你最终需要雇佣一个团队来自动化它并学习该工具。根据您的环境,这可能是一个很好的设置,但在我见过的大多数情况下,它并没有那么好。它们可能很棒,但您需要投入大量时间/金钱/人员才能使其发挥作用。 所以对于我上面的编码示例,我必须为每个步骤编写单独的测试(client.SomethingInteresting()、server.SomethingInteresting())? 是的,很有可能。这听起来可能比实际容易,因为您可能需要首先修改这些类以使其可测试,但基本上可以。我会首先找到可以轻松更新或不会危及整个系统的类。通过这种方式,您将获得(真正的)需要的感觉,然后一点一点地处理系统的其余部分。【参考方案2】:您应该研究像 Cucumber 或 Fitnesse 这样的集成测试运行器。根据您的场景,集成测试运行程序将充当服务器的客户端并对服务器进行适当的调用,或者它将与您现有的客户端共享一些域代码并运行该代码。您将为您的场景提供一系列应进行的调用,并验证输出的结果是否正确。
我认为您至少可以通过一些工作使您的应用程序的某些部分成为可测试的。祝你好运!
【讨论】:
这样的工具对 .Net 应用程序有用吗? Cucumber 似乎与 Ruby 相关(也专注于 BDD),而 Fitnesse 似乎与 Java 相关。如果它们有助于 .Net 应用程序的自动化,我有兴趣看看它们...... 这个问题似乎很有用:SpecFlow 特别是作为 Cucumber 的 .Net 替代品 - ***.com/questions/976078/cucumber-alternative-for-net 有一个使用 .Net 运行的 Fitnesse 版本。如果您使用的是独立客户端,那么使用哪种语言编写并不重要。 有一天这些将被集成到我们的构建过程中,因此尽可能接近 .NET 解决方案会更好。【参考方案3】:看看 Pex 和 Moles。 Pex 可以分析您的代码并生成将测试所有边界条件等的单元测试。您可以开始引入一个模拟框架来开始删除一些复杂的位。
为了更进一步,您必须开始使用集成测试来访问数据库。为此,您必须控制数据库中的数据并在测试完成后进行清理。这样做的方法很少。您可以在每个测试的设置/拆卸过程中运行 sql 脚本。您可以使用诸如 Postsharp 之类的编译时 AOP 框架来运行 sql 脚本,只需在属性中指定它们即可。或者,如果您的项目不想使用 Postsharp,您可以使用称为“上下文绑定对象”的内置点网功能来注入代码,以 AOP 方式运行 Sql 脚本。更多细节在这里 - http://www.chaitanyaonline.net/2011/09/25/improving-integration-tests-in-net-by-using-attributes-to-execute-sql-scripts/
基本上,我会建议的步骤,
1) 安装 Pex 并生成一些单元测试。这将是生成一组好的回归测试的最快、最省力的方法。
2) 分析单元测试,看看是否有其他测试或您可以使用的测试排列。如果是这样,请手写。如果需要,可以引入一个模拟框架。
3) 开始进行端到端集成测试。编写sql脚本将数据插入和删除到数据库中。编写将运行插入 sql 脚本的单元测试。在您的应用程序前端调用一些非常高级的方法,例如“AddNewUser”,并确保它一直到后端。前端的高级方法可以在各个层调用任意数量的中间方法。
4) 一旦你发现自己在这种模式下写了很多集成测试“运行sql脚本插入测试数据”->调用高级函数方法->“运行sql脚本清理测试数据”,你可以清理使用 AOP 技术(如 Postsharp 或上下文绑定方法)编写代码并封装此模式。
【讨论】:
+1;我使用了类似于 Moles 的工具(不公开,抱歉)并获得了一些可测试性的巨大收益。我的经验是很难理解你要替换的代码,但最终的产品是值得的。 Pex 对于快速创建基本测试用例非常有帮助。 Pex 非常适合单元测试,但目前我仅限于 Visual Studio 2008 :-(【参考方案4】:理想情况下,您会随着时间的推移重构您的代码,这样您就可以使其更具可测试性。理想情况下,这方面的任何增量改进都将允许您编写更多的单元测试,这将大大增加您对现有代码的信心,以及在不破坏现有测试代码的情况下编写新代码的能力。
我发现这种重构通常也会带来其他好处,例如更灵活和可维护的设计。在尝试证明这项工作的合理性时,请牢记这些好处。
测试这样的应用程序会失败吗?
多年来,我一直在进行自动化集成测试,发现它对我自己和我工作过的公司都非常有益:) 我最近才开始了解如何使应用程序完全可单元测试在过去 3 年左右的时间里,我正在做完整的集成测试(使用自定义实现/hack 测试挂钩)。
所以,不,这不是一个失败的原因,即使应用程序的架构是这样的。但是,如果您知道如何进行单元测试,您可以从重构应用程序中获得很多好处:测试的稳定性和可维护性、易于编写它们以及将故障隔离到导致故障的特定代码区域。
这可以使用 nUnit 或 MS Test 之类的东西来完成吗?
是的。您可以从 .Net 单元测试库中使用网页/UI 测试框架,其中包括一个内置于 Visual Studio 中的框架。
您还可以通过直接调用服务器而不是通过 UI 获得很多好处(与重构应用程序所获得的好处类似)。您还可以尝试模拟服务器,并单独测试客户端应用程序的 GUI 和业务逻辑。
此测试的“设置”是否会简单地强制启动完整的应用程序并登录?
在客户端,是的。除非你想测试登录,当然:)
在服务器端,您可以让服务器保持运行状态,或者在测试之间进行某种数据库恢复。数据库恢复很痛苦(如果你写错了,会很不稳定)并且会减慢测试速度,但它会极大地帮助测试隔离。
我是否可以简单地从数据库中重新读取 item1 和 item2 以验证它们是否已正确写入?
典型的集成测试基于Finite State Machine 的概念。此类测试将被测代码视为一组状态转换,并对系统的最终状态进行断言。
因此,您需要事先将数据库设置为已知状态,调用服务器上的方法,然后检查数据库。
您应该始终在低于您正在执行的代码级别的级别上执行基于状态的断言。因此,如果您使用 Web 服务代码,请验证数据库。如果您使用客户端并针对服务的真实版本运行,请在服务级别或数据库级别进行验证。您永远不应该使用 Web 服务并在同一个 Web 服务上执行断言(例如)。这样做会掩盖错误,并且不会让您真正相信自己正在测试所有组件的完整集成。
你们对如何开始这个令人生畏的过程有什么建议吗?
分解你的工作。识别系统的所有组件(每个程序集、每个正在运行的服务等)。尝试将它们分成自然类别。花一些时间为每个类别设计测试用例。
在设计测试用例时要牢记优先级。检查每个测试用例。考虑一个可能导致它失败的假设场景,并尝试预测它是否会是一个停止显示的错误,是否该错误可能会存在一段时间,或者是否可以将其放到另一个版本中。根据这些猜测为您的测试用例分配优先级。
确定您的应用程序中具有尽可能少的依赖项(可能是特定功能),并只为其编写优先级最高的测试用例(理想情况下,它们应该是最简单/最基本的测试)。完成后,进入应用程序的下一部分。
一旦您的应用程序的所有逻辑部分(所有程序集、所有功能)都包含在您的最高优先级测试用例中,请尽您所能让这些测试用例每天运行。在进行其他测试实施之前修复这些情况下的测试失败。
然后在您的被测应用程序上运行代码覆盖率。查看您遗漏了应用程序的哪些部分。
对每个连续的更高优先级的测试用例集重复,直到整个应用程序在某个级别上进行测试,然后你就可以发布了 :)
【讨论】:
以上是关于在 C# 中对“不可测试”的应用程序进行自动化测试。单元/集成/功能/系统测试 - 如何?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法在.net core 中对 F# 项目进行单元测试?