<返回列表

新闻分类

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