自动前端测试很棒。我们可以编写带有代码的测试以访问页面 - 或仅加载一个组件 - 并像用户一样单击“事物”或“键入文本”,然后在交互后对应用程序状态发出断言。这使我们能够确认测试中描述的所有内容都按照应用程序预期工作。
由于这篇文章大约是任何自动化UI测试的构建块之一,因此我不认为太多的先验知识。如果您已经熟悉基础知识,请随意跳过前几个部分。
有一个经典的模式在编写测试时可以知道:安排,表演,断言。在前端测试中,这转化为执行以下操作的测试文件:
在指定与内容进行交互的内容时,我们可以使用各种元素定位器来定位我们需要使用的DOM部分。
定位器可以像元素的ID,元素的文本内容或CSS选择器,例如.blog-post甚至文章> Div.Container> Div> div> div> d> p:nth-child(12)。关于可以将该元素识别为测试跑步者的元素的任何内容都可以是定位器。从最后一个CSS选择器中可以看出,定位器有许多品种。
我们经常根据脆性或稳定来评估定位者。通常,我们希望尽可能稳定的元素定位器,以便我们的测试始终找到所需的元素,即使元素周围的代码随着时间的流逝而变化。也就是说,不惜一切代价最大化稳定性可能会导致防御性测试编写实际上会削弱测试。我们通过结合脆性和稳定性的结合来获得最大的价值,与我们希望的测试所关心的内容保持一致。
这样,元素定位器就像胶带。他们应该在一个方向上真的很坚固,并在另一个方向上轻松撕裂。当对应用程序进行不重要的更改时,我们的测试应保持在一起并继续通过,但是当重要的更改发生与我们在测试中指定的内容相矛盾时,它们应该很容易失败。
首先,让我们假装我们正在写指示一个实际人来完成工作的指示。刚刚在Gate Inspectors,Inc。雇用了一名新的Gate检查员。您是他们的老板,在介绍每个人之后,您应该给他们指示检查他们的第一个门。如果您希望它们成功,您可能不会给他们写这样的笔记:
经过黄色的房子,继续前进,直到您打到迈克的母亲的朋友的山羊那时就失踪了,然后向左转,告诉我是否开着街对面的房子前面的大门。
这些方向有点像使用长CSS选择器或XPath作为定位器。这是脆弱的 - 这是“坏的脆弱”。如果黄房子被重新粉刷并重复步骤,您将无法找到大门,并且可能决定放弃(或在这种情况下,测试失败)。
同样,如果您不知道Mike的母亲的朋友的山羊状况,您将无法在正确的参考点停下来知道要检查什么门。这正是使“不好的脆性”坏的原因 - 测试可能会出于各种原因而破裂,而这些原因都与大门的可用性无关。
因此,让我们进行不同的前端测试,该测试要稳定得多。毕竟,在该地区的法律上,给定道路上的所有大门都应该来自制造商的独特序列号:
使用序列号1234转到大门,然后检查是否打开。
这更像是通过其ID找到一个元素。它更稳定,只是一个步骤。上次测试的所有故障点都已删除。只有在该ID的门未按预期打开的情况下,此测试才会失败。
现在,事实证明,尽管没有两个门在同一条道路上应该具有相同的ID,但实际上并没有在任何地方和一天都执行,但道路上的另一个大门以相同的身份结束。
因此,下一次新雇的门检查员去测试“门1234”时,他们首先发现另一个,现在正在访问错误的房屋并检查错误的事情。该测试可能会失败或更糟:如果该门按预期工作,则测试仍然通过,但没有测试预期的主题。它提供了错误的信心。即使我们原来的目标门在半夜被盗贼偷走了,它也会继续通过。
在这样的事件发生后,很明显,ID并不像我们想象的那么稳定。因此,我们做了一些下一层次的思考,并决定在大门的内部,我们想要一个特殊的ID来进行测试。我们将派出一项技术,将特殊ID放在这条门上。新的测试说明看起来像这样:
使用测试ID“ MyFauite Gate”进入大门,然后检查是否打开。
这就像使用流行的数据测试属性。这样的属性很棒,因为在代码中很明显,它们旨在通过自动测试使用,不应更改或删除。只要门具有该属性,您就会始终找到大门。就像ID一样,唯一性仍然没有强制执行,但更有可能。
这距离您可以获得的脆弱距离远远遥不可及,并且可以确认大门的功能。除了我们故意添加用于测试的属性外,我们不依赖任何东西。但是这里隐藏了一些问题……
这是门的用户界面测试,但是定位器是用户无法使用该门的东西。
这是一个错过的机会,因为在这个虚构的县,事实证明,大门必须在上面印有房屋号码,以便人们可以看到地址。因此,所有大门都应该具有独特的面向人类的标签,如果没有的话,这本身就是一个问题。
在使用测试ID定位大门时,如果事实证明大门已被重新粉刷并覆盖了房屋号,我们的测试仍然会通过。但是大门的重点是让人们进入房屋。换句话说,用户找不到的工作门仍然应该是测试故障,我们希望能够揭示此问题的定位器。
这是该测试检查员在第一天的测试指令上的另一项通行证:
前往大门40号房屋,检查是否打开。
该使用一个定位器为测试增值:这取决于用户也取决于某些东西,这是Gate的标签。它增加了一个潜在的原因,即测试在达到我们要实际测试的相互作用之前失败,这乍一看似乎很糟糕。但是在这种情况下,由于定位器从用户的角度有意义,因此我们不应该将其耸耸肩,因为它是“脆弱的”。如果该大门无法通过其标签找到,那么它是否打开并不重要 - 这是“脆性的好”。
许多前端测试建议都告诉我们避免写作依赖DOM结构的定位器。这意味着开发人员可以随着时间的推移来重构组件和页面,并让测试确认面向用户的工作流程没有破坏,而无需更新测试即可赶上新结构。这个原则很有用,但是我会有所调整要说的是,我们应该避免写作依赖于前端测试中无关的DOM结构的定位器。
为了使应用程序正确函数,DOM应反映在任何给定时间屏幕上内容的性质和结构。原因之一是可访问性。从这个意义上讲,正确的DOM可以使辅助技术正确解析并向未看到浏览器呈现的内容的用户进行描述更容易。 DOM结构和普通的旧HTML对依靠辅助技术的用户的独立性产生了巨大的影响。
让我们旋转前端测试,以提交某些内容,以提交我们应用程序的联系表。我们将使用柏树为此,但是选择定位器的原则在战略上适用于使用DOM来定位元素的所有前端测试框架。在这里,我们找到元素,输入一些测试,提交表格并验证“感谢”状态:
//?不建议 cy.get('#名称')。类型('mark') cy.get('#comment')。类型('test评论') cy.get('。submit-btn')。click() cy.get('。谢谢你')。应该('be.visible')
这四行中发生了各种隐式断言。 cy.get()正在检查该元素是否存在于DOM中。如果元素在一定时间之后不存在,则测试将失败,而诸如类型的操作并单击验证元素是否在采取操作之前可见,启用和毫无疑问。
因此,即使在这样的简单测试中,我们也会获得很多“免费”,但是我们还对我们(和我们的用户)并不真正在乎的事情介绍了一些依赖性。我们正在检查的特定ID和类似乎足够稳定,尤其是与Div.main> p:nth-child(3)> span.is-a-a-button之类的选择器相比。这些长的选择器是如此具体,以至于对DOM的次要更改可能会导致测试失败,因为它找不到元素,而不是因为功能已损坏。
但是,即使是我们的简短选择器,例如#NAME,也遇到了三个问题:
对于第一和第二问题,建议的解决方案通常是使用HTML中专门的数据属性,这些属性仅用于测试。这是更好的,因为我们的测试不再取决于DOM结构,并且随着开发人员修改组件周围的代码,只要将数据测试保留在右输入元素上,测试就会继续通过而无需更新,而无需更新。
不过,这并不能解决问题三 - 我们仍然有一个前端交互测试,该测试取决于对用户毫无意义的事物。
当元素定位器依赖我们实际想要依靠的东西时,它们是有意义的,因为有关定位器对用户体验很重要。在交互元素的情况下,我认为使用的最佳选择器是该元素的可访问名称。这样的东西是理想的:
//?受到推崇的 cy.getByLabelText('name')。类型('mark')
此示例使用柏树测试库中的副词架。 (实际上,如果您以任何形式使用测试库,它可能已经在帮助您像这样编写可访问的定位器。)
这很有用,因为现在内置检查(我们通过CY.Type()命令免费获得)包括表单字段的可访问性。所有互动元素都应具有接触辅助技术的可访问名称。例如,ScreenReader用户会知道他们输入的表单字段的方式是为了输入所需的信息。
为表单字段提供此可访问名称的方法通常是通过与该字段相关联的标签元素。来自柏树测试库的getBylabelText命令验证了该字段是否适当标记,但该字段本身是允许具有标签的元素。因此,例如,以下HTML在尝试()命令之前都将正确失败,因为即使存在标签,标记Div是无效的HTML:
可编辑的div元素: <div contenteaditable="“" true></div>
由于这是无效的HTML,因此ScreenReader软件永远无法正确将此标签与此字段相关联。为了解决此问题,我们将更新标记以使用真实的输入元素:
真实输入:
这很棒。现在,如果在对DOM进行编辑后,此时测试失败了,这并不是因为结构无关紧要,而不是安排元素的方式,而是因为我们的编辑做了一些破坏DOM的部分,而我们的前端测试显式关心,这实际上对用户很重要。
对于非交互元素,我们应该戴上思维上限。让我们在回到数据-CY或数据测试属性之前先使用一些判断,如果DOM根本不重要,它将永远在我们身边。
在我们浸入通用定位器之前,让我们记住:DOM的状态是我们作为Web开发人员的整个事物™(至少,我认为是)。 DOM为没有视觉上页面的每个人驱动UX。因此,很多时候,我们可能会在代码中使用或应该在测试定位器中包含一些有意义的元素。
如果没有,因为。假设应用程序代码是所有通用容器,例如DIV和SPAN,我们应该在添加测试时先考虑首先修复应用程序代码。否则,实际上有测试的风险实际上指定了通用容器的期望和期望,这使得某人更难修改该组件更容易访问。
此主题为组织中可访问性的工作方式打开了一罐蠕虫。通常,如果没有人在谈论它,而不是我们公司实践的一部分,那么我们就不会认真对待可访问性作为前端开发人员。但是归根结底,我们应该是设计的“正确标记”的专家,以及在决定这一点时要考虑的事情。我在Connect.Tech 2021中讨论了更多的事情,称为“研究和写作可访问的Vue…Thingies”。
正如我们在上面看到的那样,传统上我们认为互动的要素,有一个很好的经验法则很容易在我们的前端测试中构建:交互式元素应该具有与元素正确关联的可感知标签。因此,当我们对其进行测试时,任何交互式都应使用所需标签从DOM中选择。
对于我们不认为是交互式的元素(就像大多数内容渲染的元素一样,除了Main之类的一些基本地标之外,我们都不会触发任何灯塔审计失败,如果我们将大部分非相互作用内容放入通用DIV或跨度容器中。但是,标记对辅助技术的信息和帮助并不是很有帮助,因为它没有向看不到它的人描述内容的性质和结构。 (要看到这一点,请查看Manuel Matuzovic的出色博客文章:“建立最不可能的网站,并以完美的灯塔得分。”)
我们渲染的HTML是我们向任何不视觉上感知内容的人传达重要上下文信息的地方。 HTML用于构建DOM,DOM用于创建浏览器的可访问性树,可访问性树是各种辅助技术可以用来表达内容的API,可以使用我们的软件来表达内容和可以采取的操作。 ScreenReader通常是我们想到的第一个示例,但是可访问性树也可以由其他技术使用,例如将网页变成盲文等的显示。
自动化可访问性检查不会告诉我们我们是否真的为内容创建了正确的HTML。 HTML的“正确性”我们正在向开发人员发出有关在可访问性树中需要传达哪些信息的开发人员。
一旦打了电话,我们就可以决定其中多少适合烘烤到自动的前端测试中。
假设我们已经决定,一个具有状态ARIA角色的容器将持有“谢谢”和错误消息传递联系表。这可能很好,因此ScreenReader可以宣布表格成功或失败的反馈。可以应用.Thank-you和.error的CSS类来控制视觉状态。
如果我们添加此元素并想为其编写UI测试,则在测试填写表格并将其提交后,我们可能会编写这样的断言:
//?不建议 cy.get('。谢谢你')。应该('be.visible')
甚至是使用非脆性但仍然毫无意义的选择器的测试:
//?不建议 cy.get('[data-Test]')。应该('be.visible')
两者都可以使用cy.contains()重写:
//?受到推崇的 cy.contains('[cool =“ status”]',',谢谢,我们收到了您的消息') 。
这将确认预期的文本出现并在正确的容器中。与先前的测试相比,这在验证实际功能方面具有更大的价值。如果此测试的任何部分失败,我们想知道,因为消息和元素选择器对我们很重要,不应琐碎地更改。
我们肯定在这里获得了一些覆盖范围,没有很多额外的代码,但是我们也引入了另一种脆弱性。我们的测试中有简单的英语字符串,这意味着,如果“感谢”信息会更改为“谢谢您伸出手!”之类的东西。该测试突然失败了。与所有其他测试相同。对标签的编写方式的一个很小的更改将需要更新使用该标签的元素的任何测试。
我们可以通过在前端测试中使用相同的真实来源来改善这一点,与我们在代码中所做的相同。而且,如果我们目前在组件的HTML中嵌入了可读的句子……那么,现在我们有理由将这些东西从那里拉出。
一个神奇的数字(或较少引起的“未命名数值常数”)是您有时在代码中看到的超特定值对计算的最终结果很重要,例如一个好的旧1.023033或其他东西。但是由于这个数字没有标记,因此其意义尚不清楚,因此目前尚不清楚它在做什么。也许它适用税收。也许它补偿了我们不知道的一些错误。谁知道?
无论哪种方式,前端测试也是如此,一般建议是避免魔术数字,因为它们缺乏清晰度。代码评论通常会抓住它们,并询问该号码的目的。我们可以将其转移到常数吗?如果要重复使用多个位置,我们也会做同样的事情。我们可以将其存储在变量中并根据需要使用变量,而不是在任何地方重复该值。
多年来,编写用户界面,我来查看HTML或模板文件中的文本内容与其他上下文中的魔术数字非常相似。我们通过我们的代码将绝对价值放置,但实际上,将这些值存储在其他地方并在构建时间(甚至通过API)将这些值存储可能更有用。
有几个原因:
{{content [currentlanguage] .contactform.name}}}
const text = content.en.contactfrom //我们将进行一次,并且文件中的所有测试都可以从中读取 cy.contains(text.nemelabel,'[remo =“ status”]')。应该('be.visible')
每种情况都是不同的,但是在编写强大的UI测试时,拥有一定的字符串常数是一项巨大的资产,我建议这样做。然后,如果以及何时对我们的情况需要翻译或动态内容,那么我们为此做好了更好的准备。
我也听说过反对在测试中导入文本字符串的好论点。例如,如果测试本身独立于内容源指定预期内容,则某些发现测试更可读性,并且通常更好。
这是一个公平的观点。我对此的说服力不大,因为我认为应该通过更多的社论审查/发布模型来控制内容,并且我希望测试检查源的预期内容是否得到了渲染,而不是一些在编写测试时正确的特定字符串。但是很多人在这方面不同意我的意见,我说只要在一个团队中,权衡就可以理解,这两种选择都是可以接受的。
也就是说,将代码与前端的内容隔离开来仍然是一个好主意。有时,混合和匹配甚至可能是有效的 - 就像在我们的组件测试中导入字符串,而不是在我们的端到端测试中导入它们。这样,我们节省了一些重复,并获得了信心,即我们的组件显示正确的内容,同时仍进行了前端测试,这些测试以编辑,面向用户的意义独立主张预期的文本。
[data-test =“ Success-Message”]之类的CSS选择器仍然很有用,并且在有意使用的情况下使用,而不是一直使用。如果我们的判断是没有有意义的,可访问的方法来定位元素,那么数据测试属性仍然是最佳选择。它们比例如巧合的巧合要好得多,就像您在编写测试的那天碰巧发生的任何DOM结构一样,然后回到“第三个Div中的第二个列表中的第二个列表中,具有一类卡片”样式的测试样式。
有时候,期望内容具有动态性,并且没有办法轻松地从某些共同的真理来源中获取字符串,以便在我们的测试中使用。在这些情况下,数据测试属性有助于我们达到我们关心的特定元素。例如,它仍然可以与可访问性友好的断言结合在一起:
cy.get('h2 [data-test =“ intro-subheading”]')
在这里,我们想找到什么具有介绍性数据测试属性的内容,但仍然允许我们的测试断言,如果我们希望它是H2元素,则应该是H2元素。数据测试属性用于确保我们获得我们感兴趣的特定H2,而不是网页上可能存在的其他H2,如果由于某种原因,该H2的内容在测试时就无法知道。
即使在我们确实知道内容的情况下,我们仍可能使用数据属性来确保应用程序在正确的位置呈现:
cy.contains('H2 [data-test =“ intro-subheading”]','欢迎进行测试!')
数据测试选择器也可以方便地到达页面的某个部分,然后在其中做出断言。这看起来如下:
cy.get('Artical [data-test =“ ablum-card-blur-great-escape”]')。在(()=> { cy.contains('H2',“大逃生”)。应该('Be.be Visible') Cy.Contains('P','1995专辑Blur')。应该('Be.visible') cy.get('[data-test =“ stars”]')。 }))
到那时,我们陷入了一些细微差别,因为可能还有其他瞄准此内容的好方法,这只是一个例子。但是归根结底,如果担心这样的细节,这是一个很好的选择,因为至少我们对HTML中嵌入的可访问性功能有所了解,并且我们希望将这些功能包括在测试中。
如果我们对如何讲述测试如何与哪些元素进行互动以及期望的内容有什么想法,前端测试为我们带来了更多的价值。如果这些内容与功能相关,我们应该更喜欢可访问的名称而不是目标交互式组件,并且应包含特定的元素名称,ARIA角色等(ARIA角色等)。这些定位者在实用的情况下创造了力量和脆弱的正确组合。
当然,对于其他所有内容,都有数据测试。
以上是最大化您的前端测试定位器的详细内容。更多信息请关注PHP中文网其他相关文章!