全国服务热线:
0592-5794349
当前位置:首页> 新闻中心

软件开发公司全栈测试:平衡单元测试和端到端测试

* 来源: * 作者: * 发表时间: 2019-10-10 12:32:14 * 浏览: 0
软件开发公司全栈测试:平衡单元测试和端到端测试全栈开发人员的特点是能够从头到尾交付和发布功能。教程和书籍通常侧重于构建一个全栈开发环境,并让测试做“事半功倍”(我使用Angular,Rails,Bootstrap和Postgres)。但是,通常缺少有关如何在整个Web开发堆栈中测试应用程序的指导。让我们深入研究下一篇文章。我们将学习如何充分利用端到端测试,包括有关测试内容以及如何确保这些测试的可靠性和可维护性的指南。我们还将讨论单元测试及其在端到端测试策略中的作用。但是首先,我们需要了解编写测试的基本目的。从根本上说,测试是为了确保应用程序的行为符合开发人员的意愿。它们是执行代码并检查其行为是否符合预期的自动化脚本。测试编写得越好,您就越可以依靠它们进行部署。如果测试不充分,则需要QA团队或有缺陷的软件(这两者都意味着用户获得价值的速度比理想情况要慢得多)。如果测试足够,则可以放心,快速地发布,而无需批准或缓慢的人工检查(如质量检查)。对于编写的测试,您还必须权衡将来的可维护性。应用程序将更改,因此测试将更改。在理想情况下,测试的修改与软件的修改成正比。如果您修改错误消息,则不需要大量重写测试套件。但是,如果您完全修改了用户流程,则可以预期会有很多测试需要重写。实际上,这意味着您不能将所有测试都当作端到端的完全集成测试,而不能仅仅做糟糕的单元测试。这是关于如何实现这种平衡。测试类型测试类型很多,但是在本文中,我们讨论两种类型:端到端测试和单元测试。端到端测试可模拟用户行为。在Web应用程序中,他们启动服务器,打开浏览器,单击,然后断言浏览器中发生了某些特定的事情,让我们相信该功能有效。这些测试使我们充满信心,但是它们缓慢而脆弱,并且与用户界面紧密耦合。单元测试根据代码单元的通用API运行它们。这些测试需要创建一个类的实例,该实例使用特定的输入来调用其方法,并断言所调用的方法已经达到了预期的效果(通常返回预期的输出)。这些测试快速,稳定,并且与系统的其余部分没有紧密耦合。但是,它们不允许您相信整个系统可以运行mdash,mdash,只有经过测试的代码单元可以正常运行。构建功能的任务是在两种类型的测试之间找到适当的平衡。如果端到端测试过多,将来修改应用程序将很痛苦且缓慢。如果数量太少,那么即使快速测试套件的代码覆盖率为100%,也会有一些无法检测到的缺陷会进入生产环境。从用户体验入手您的软件是为特定用户提供服务,因此用户应促进您的工作。我不建议您使用测试来设计用户体验,因此请在编写测试之前弄清楚用户将如何使用该软件(通过实验代码或与同一设计师合作)。一旦解决,就可以开始工作。理想情况下,您将为部分用户体验创建一个端到端测试,并编写代码以通过该测试。在编写代码时,您将创建单元测试,这些单元测试指定需要创建或修改的代码的规范(通常是后者)。问题在于,很难编写没有用户界面工件(HTML)作为参考的端到端故障测试。这是因为大多数端到端测试采用以下形式:在页面上找到一个元素,以某种方式与之交互,确认交互成功,然后重复该过程直到测试结束。这意味着您需要围绕将要交互的用户界面元素(DOM对象)制定一些规范。考虑基于JavaScript的交互设计时,它甚至可以至少部分地难以测试您是否未真正构建接口。为此,请在浏览器中运行粗略的UI轮廓。使用预先准备的数据,无需考虑替代过程mdash,mdash一次只关注一件事。一旦启动并运行,就可以编写测试。为此,需要考虑两件事:是否需要测试此功能?如果需要如何测试?测试内容尽管在编程中没有令人愉快的途径,但是用户所经历的代码路径比代码小得多。例如,当用户购买产品时,根据用户的地址,所选的送货方式或以前的购买历史记录,我们可能会以其他方式处理订单。在所有情况下,用户体验都是相同的,因此在用户看来,只有一个过程。此时,您的目标是测试所有用户进程。您需要一个测试套件,该套件可以模拟用户执行您想要和想要他做的事情,并断言您要提供给该用户的所有体验都运行良好。如果您已经知道要测试什么,应该怎么做?如何进行端到端测试如果您修改流程,则需要修改该流程的测试。由于端到端测试模拟了用户活动,因此无需为要声明的所有内容编写测试。如果用户应该在计费界面上看到三条重要信息,则无需编写mdash,mdash的三个测试,而只需编写一个测试来检查所有三条信息就足够了。因此,在修改现有用户体验时,请寻找现有的改进测试。否则,需要新的测试。请记住,您的目标是模拟用户必须执行的操作。对于如何组织测试中的导航和行为,请务必保持开放和诚实。用户是否真的直接导航到某些深层链接?还是他们单击通用的起始页面以转到所需的位置?这很难做到,特别是如果您使用较少的标签来做到这一点。测试需要定位特定的DOM元素才能与其进行交互,而准确地找到要与之交互的元素并不总是那么容易(或不可能)。您需要ldquo,路标。标识符专门插入到DOM中以定位感兴趣的元素。确保尽早确定这些徽标的工作方式。您不应使用最初用于样式设置的CSS类来定位DOM元素。这样做意味着前端开发人员更改类名称将破坏测试。您也不应使用JavaScript代码使用的CSS类或数据属性(例如以js-开头的类)。这将带来相同的损害。使用以test-为前缀的CSS类或以data-test为前缀的属性是两种常见的技术:对于hellip,hellop以及确实如此,这似乎不舒服。但是,这比将测试耦合到内容或表示类要麻烦得多。在这里,您需要找到一个平衡的mdash,mdash,不要盲目地用data-test属性标记每个元素。例如,如果您想单击某个按钮来购买特定产品,那么您真正需要做的就是找到一个包含该产品和购买按钮的元素。添加data-test-product属性后,您可以使用CSS选择器,例如[data-test-product = 39,123439,] input [type = 39,submit39,]找到产品1234的购买按钮。这意味着您必须修改仅用于测试的标记,也就是说,为了获得您提供的用户体验,用户需要下载一些不需要的字节。这是一个平衡,但是比不良的测试覆盖率更好(对用户的损害远远超过了HTML中的额外字节)。正确无误。当页面上发生交互而无需重新加载即可更改页面内容(换句话说,使用JavaScript)时,此技术就显得尤为重要。处理交互每次单击重新加载页面时,端到端测试更加可靠,因为底层工具知道等待页面重新加载。当用户交互只是更改DOM时,难度很大,因为该工具不知道“什么”,正在发生的事情,它无法“等待”完成。当测试需要不基于用户操作重新加载的相同页面交互时,需要一种方法来等待DOM操作完成,然后再启动断言。如果您不等待,测试将不必要地失败,如果测试开始生效,则DOM尚未更新。正如您使用标记来定位要在标记中操作的DOM元素一样,我们也可以在此处使用它们。如果交互失败或没有发生,则任何新的或更改的标记都应具有不会出现的某种身份。换句话说,您不必在测试中睡觉就可以等待DOM事件调用mdash,mdash,DOM应该包含一个可以在测试中显式等待的标识符。例如,假设我们要测试一个为用户生成成功消息的动作。假设实现是发出AJAX请求,并在调用结束时将消息插入DOM。一个基本的实现可以做到这一点:functionpurchase(productId){$ .post(quot,/ products / quot ,, {quot,idquot,:productId}).done(function(){$(quot..header”,) .html(quot,Yourorderwasplaced quot,)}} .fail(function(){$(quot..headerquot,)。html(quot,Therewasaproblem quot,),}),您可以配置测试以等待使用CSS类alert-success元素出现,然后声明其内容。这意味着,如果页面需要使用该类的任何其他元素,则测试将不可靠或损坏。尽管您可以将其限制为HTML标头,但这只是一个慢速计划。相反,您可以使用data-test-property:functionpurchase(productId){$ .post(quot,/ products / quot,{{,idquot,:productId}).done(function(){$(quot。)。 headerquot,)。html(quot,Yourorder被替换为quot,),})。fail(function(){$(quot..headerquot,)。html(quot,Therewasaproblem quot,),}),尽管这会增加标记的字节数,但是它使您可以编写可靠的测试,不受某些视觉变化的影响。只要页面流在成功购买后显示一条消息,就可以修改可视化实现而不破坏测试。这就是您想要的,这是一个折衷。您还可以牺牲这种信心并创建越来越小的标记,但是当显示更改时,您要么花时间修复测试,要么被迫手动进行质量检查,或者发布未经测试的软件。当今的端到端测试工具(如Capybara)包含您需要的所有功能。它提供了一种在继续测试过程之前,等待DOM元素出现,声明页面特定部分的内容以及与表单元素进行交互的方式。大多数其他Web应用程序堆栈中都可以使用类似的工具。无论哪种方式,您都可以将测试库与非接口浏览器(例如PhantomJS)结合使用,以实现出乎意料的快速和可靠的端到端测试。另一个值得注意的地方是如何在分布式环境中执行此操作。当“一个以上”应用程序不止一个时,当测试一个整体系统时,以上技术完全足够。但是,如果您要测试更分散的系统,情况将更加复杂。假设您正在处理面向客户的应用程序,但是它必须从另一个系统获取库存数据。您如何为此编写测试?首先,请记住您要测试的内容。端到端测试是测试用户交互。这意味着端到端测试不负责声明远程服务的功能,也不负责声明应用程序正在正确使用远程服务。检验服务消费的更好方法是使用“ ldquo”,即消费者驱动的合同(消费者)驱动的合同),这是单元测试的一种形式(至少在我在本文中所做的广义定义中)。在端到端测试中如何模拟远程服务仍然没有定论。您可以构建该服务的真实版本,但这不是很好。您必须管理该服务及其依赖的服务的内部数据存储。这将使复杂性迅速增加并且难以管理。常见的选择是使用HTTP层仿真系统。在Ruby中,VCR是具有此功能的工具。您记录与真实服务的交互以建立往返HTTP协议,然后在以后运行测试时,仿真系统将回放记录的交互,而无需使用网络。如果单元测试覆盖了正确的服务消耗,那么这对于端到端测试非常有效。另一种选择是构建一个简化的模拟服务,该服务返回预先准备的数据。该应用程序照常进行HTTP调用,但是会调用预先准备的服务,并且仅将静态已知的数据返回给该应用程序。这个要求预先进行一些配置,但是对于简单的服务交互非常有效。如果应用程序需要在服务中存储状态并且具有长的往返对话,那么该技术会有点困难。我的建议是尝试首先模拟HTTP,因为那样既简单又快速。既然我们知道要进行哪些测试以及如何进行端到端测试,那么单元测试又如何呢?单元测试回想一下,我们的标准是用户端到端测试的流程。这个想法是,虽然整个系统具有许多可能的逻辑过程,但对用户体验的影响却要小得多。单元测试是关于测试其余逻辑流的。这使我们能够快速而可靠地断言大多数系统功能的正确行为。换句话说,尽管我们可以使用端到端测试来断言整个系统中的每个可能过程,但这不是必需的,并且会非常缓慢且脆弱。例如,假设计费功能具有两个用户流程:一个是成功购买,一个是购买失败,并且用户必须重试。将有两个端到端测试。让我们进一步假设后台有以下可能性:客户的信用卡已正确借记,客户的银行通讯出现问题,但我们要假装成功,然后再借记客户的借记卡。的信用卡被拒绝,客户的信用卡已过期。这是四个过程,因此我们希望有四个单元测试可以断言它们已正确处理。是的,将有重复报道。在端到端测试中,我们可能会创建两个用户流程,这些用户流程可以成功借记和拒绝这两个测试以处理该功能,因此在编写单元测试时,我们的研究范围将超出理论要求。同样,这是一个折衷,但是单元测试必须很好地覆盖您的课程,这一点很重要。这使他们可以更改位置,使用并且更容易修改。关于如何编写单元测试的理论很多,远远超出了我们在此讨论的范围。我的建议是使用一种对您有用并且易于向他人解释的技术,并始终使用它。对于单元测试,最困难的部分是确定考虑将代码设计用于测试的程度。这类似于我们将属性和其他标识符添加到HTML到mdash,mdash的方式,而这些工件仅是因为我们要测试而存在。在编写单元测试时,您将面临相同的选择。例如,假设“购买者”类实现了信用卡借方代码。假设它将使用第三方提供的AwesomePayments进行实际借记。 classPurchaser defcharge(购买)AwesomePayments.charge(购买。customer.id,purchase.amount)rescue = gt,ex try_again_later(purchase.id)end ... end上面的代码清晰易懂,无需进行单元测试,这可能是一个更好的设计。但是,为了简化测试,我们可能希望控制AwesomePayments的实例:classPurchaser取消初始化(awesome_payments = AwesomePayments)@ awesome_payments = awesome_payments结束付款(购买)@ awesome_payments.charge(purchase.customer.id,purchase.amount) ,例如try_again_later(purchase.id)结束现在,您可以在测试过程中传入AwesomePayments的模拟实现,以更好地控制测试。测试影响了我们的设计(尽管影响很小)。您甚至可以说此类是更好的代码。但这并非总是如此。我将使用与您进行端到端测试相同的标准:做些使生活更轻松的事情,但不要过分地做,一定要做好。