联系方式

联系方式
电话:0592-5794349
业务咨询:17350028151 15359409915
QQ咨询:1803977211 491666614
地址:福建省厦门市湖里区岐山路一号亿华中心608A
当前位置:首页> 新闻中心

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

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