自動前端測試很棒。我們可以編寫帶有代碼的測試以訪問頁面 - 或僅加載一個組件 - 並像用戶一樣單擊“事物”或“鍵入文本”,然後在交互後對應用程序狀態發出斷言。這使我們能夠確認測試中描述的所有內容都按照應用程序預期工作。
由於這篇文章大約是任何自動化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中文網其他相關文章!