通過視覺測試保持端到端質量

已發表: 2022-03-10
快速總結 ↬通過在測試中添加視覺元素,您可以獲得更多選項來添加有意義的方式來保持應用程序的高質量。 Colby Fayock 解釋瞭如何。

測試是任何開發人員工作流程的關鍵部分。 它幫助我們確保我們的項目將保持高水平的質量,並防止任何討厭的錯誤進入野外。

但通常自動化測試管理起來很麻煩。 在無窮無盡的代碼以確保您提供全面覆蓋和處理前端測試的脆弱性之間——一個簡單的選擇器更改可能會完全破壞端到端的工作流程——有時感覺就像一個上坡路戰鬥。

通過添加自動化視覺測試,我們可以消除那些不穩定的測試,通過利用我們網站或應用程序的屏幕截圖的智能圖像比較來提升我們的測試管道以提供覆蓋(以及更多)。

在我們深入了解可視化測試之前,讓我們花一點時間來回顧一下不同類型的自動化測試以及它們如何組合在一起。 然後,我們將討論究竟什麼是可視化測試,以及它如何為您的項目提供另一個級別的測試覆蓋率。

快速了解一些類型的自動化測試

自動化測試是開發週期中一個有趣的部分。 一些客戶或利益相關者能夠清楚地看到他們提供的價值,但其他人更願意將任何開發時間花在純粹的功能開發上。

這有時可能是違反直覺的,其中自動化測試的目標是保護業務或防止團隊不得不首先花時間修復錯誤。 編寫可靠的自動化測試可以防止重大經濟損失! 最終,有些人比其他人更願意承擔風險。

幸運的是,這種價值並不總是很難出售,當我們有時間專注於質量自動化測試時,我們有多種選擇來處理這些測試,如單元測試、集成測試、端到端測試和視覺測試(也可以為前三個提供擴展的覆蓋範圍)。

當應用每種測試的優勢時,我們能夠花更多的時間編寫測試,這些測試實際上可以幫助保護我們的工作並避免客戶的挫敗感。

讓我們看一下這些測試策略中的一些在實踐中的樣子。

單元測試

單元測試側重於測試應用程序的較小的、集中的區域。 想要測試促銷後計算訂單總額的功能是否正常工作? 你想寫一個單元測試。

 function myBusinessLogic(pricePerItem, quantity, discount) { const subtotal = pricePerItem * quantity; return subtotal - ( discount * subtotal ); } expect(myBusinessLogic(2, 4, .1)).toEqual(7.2);

單元測試最重要的部分是它們編寫起來很便宜,並且不需要很多時間來運行。 這就是為什麼您經常會看到公司花費大量時間構建一套單元測試來捕獲應用程序的這些細粒度部分。

但是由於集中測試,單元測試可能無法涵蓋這些不同部分如何協同工作,這就是我們開始進入集成測試的地方。

集成測試

集成測試的目標是獲取應用程序的較小部分和組件並測試它們如何協同工作。 一個常見的例子可能是 UI 的特定部分如何響應交互,然後是對服務器或數據庫的請求。

 cy.get('.add-to-cart').click(); cy.url().should('contain', 'cart'); cy.get('.cart li').contains('My Item');

您的小型 UI 組件完全有可能按預期工作。 您的合成事件可能會在 onClick 實例上正確觸發。 包裝您的 API 請求的代碼可能會與一些模擬數據完美地執行。 但是這兩個部分之間可能存在單元測試可能無法捕捉到的漏洞。

集成測試是測試應用程序的一種令人信服的方法,但是當您希望測試“所有東西”時,您可以更進一步。

端到端測試

端到端測試捕獲整個用戶的端到端旅程,以實現集中的工作流程。 例如,如果我正在建立一個電子商務商店,那麼“愉快的路徑”(或阻力最小的預期路徑)將是找到一種產品,將其添加到購物車中,然後為這些商品付款。 如果我正在編寫端到端測試,我會記錄整個過程,即從在產品列表頁面上找到產品到為該項目付款。

 cy.visit('/products'); cy.get('.product a[href="/product/1234"]').click() cy.url().should('contain', 'product/1234'); ... cy.get('.order-button').click(); cy.url().should('contain', 'receipt'); cy.get('.receipt li').contains('My Item');

端到端測試的重要部分在於它本質上是一個大型集成測試。 您正在捕獲應用程序的許多不同組件,包括 UI 的工作方式、API 是否正確響應以及這些部分是否協同工作。

問題是端到端測試,甚至集成測試,需要更多的時間來編寫,而且它們也需要更長的時間來運行。 那麼,我們如何才能利用我們所有的測試選項,並將一套測試組合在一起,從而提供一種有效的方式來覆蓋我們的應用程序呢?

利用不同類型的測試

有多種心態通常描述了您應該花時間編寫的每種類型的測試數量。

Mike Cohn 在他的《敏捷的成功》一書中提出了“測試金字塔”的概念。

具有測試類型的金字塔形狀
金字塔,包括 UI、服務和單元測試(大預覽)

他認為你應該編寫更多的單元測試,它們的編寫成本更低,運行速度更快。 雖然他的原始圖表對各種測試的標記略有不同,但隨著您更傾向於集成類型的測試,它們的運行速度會變得更慢,編寫成本也會更高。 雖然這些測試很有價值,但您不希望擁有與單元測試一樣多的集成或端到端測試。

保持這種平衡可以幫助您專注於捕獲應用程序的關鍵部分,例如帶有單元測試的業務邏輯以及它們如何與集成測試一起工作,但 Kent C. Dodds 認為測試技術已經趕上了沒有編寫集成測試需要更大的成本權衡,這就是他的“測試獎杯”概念的用武之地。

具有測試類型的獎杯形狀
獎杯包括端到端、集成、單元和靜態(大預覽)

在現代開發環境中,我們可以使用許多令人驚嘆的工具,例如 Cypress、Selenium 和 Playwright,它們都使開發人員和 QA 工程師能夠編寫與 Chrome 和 Firefox 等瀏覽器輕鬆交互的測試。

使用 Cypress,編寫一個單擊按鈕的測試看起來很簡單:

 cy.get('#my-button').click()

這可以說就像測試按鈕是否與合成事件一起工作一樣簡單,如果不是更簡單的話。 最好的部分是您正在測試該按鈕在瀏覽器中的實際工作方式。

無論您訂閱哪個圖表,最終目標都是權衡成本和速度之間的不同選項,以確定適合您的特定應用程序。 重要的是不僅要在您的報導報告中達到 100%,而且要真正確保您為訪問者提供的體驗能夠正常工作。

但無論您運行的測試組合如何,這些僅與 DOM(文檔對像模型)交互和測試的程序化測試都缺少一大塊難題:訪問者如何直觀地看到該應用程序。

傳統類型的測試沒有捕捉到什麼

當您在應用程序上運行單元、集成和端到端測試時,它們都有一個共同點。 他們都在測試代碼。

我的意思是他們沒有測試您的應用程序的訪問者實際看到的內容。

如果您正在運行一套集成測試,並且像我們之前的示例一樣,測試某人可以將產品添加到購物車併購買它,那麼每一步,您都會通過代碼在 DOM 中找到一個元素並確認它以同樣的方式工作。

這不會測試頁面上的文本是否清晰等內容。 是否有人添加了一個 CSS 更改,意外地將所有東西浮動到左側並將它們顛倒過來?

具有重疊帖子的 Instagram 應用程序,包括贊助
Instagram 帖子全部飄到一個角落(大預覽)

這些類型的錯誤被稱為“視覺錯誤”,它們可能會出色地通過您的所有測試,但當有人真正看到它時,它並不完全正確或更糟,完全無法使用。

實際上,我們不能指望通過傳統測試對用戶界面的每個細節提供 100% 的全面覆蓋。 在無窮無盡的應用程序狀態和我們一直在添加新功能的事實之間,它根本無法擴展。

這就是將我們引向這個故事的標題的原因:視覺測試

跳躍後更多! 繼續往下看↓

什麼是視覺測試?

可視化測試捕獲應用程序的可見輸出(如屏幕截圖),並將其與另一個時間點的同一應用程序進行比較。

這通常通過首先捕獲基線屏幕截圖或先前捕獲的具有預期結果的應用程序實例,然後將每個新測試與該基線進行比較來實現。

顯示頁面差異的可視化測試儀表板
頁面上由於錯誤導致的視覺差異(大預覽)

但是隨著您的項目的開發,情況會發生變化。 隨著時間的推移,當您批准新的視覺差異作為已接受的更改時,該基線將與您的應用程序一起更新。

視覺測試的類型

關於可視化測試的一個有趣的事情是有不同的方法來處理這個問題。

視覺測試的一種方法是使用逐像素比較,其中測試框架將標記出它在兩個圖像之間看到的任何差異。 雖然這樣的比較提供了視覺測試的入門級,但它往往是不穩定的,並且可能導致很多誤報。

您可以想像,在使用 Web 時,頁面加載和瀏覽器更新之間的呈現方式往往會略有不同。 如果瀏覽器由於渲染更改、文本光標顯示或“僅僅因為”而將頁面渲染了 1 個像素,則您的部署可能會由於這些失敗的測試而被阻止。

在處理動態內容時,您的測試也容易失敗。 例如,如果您每天在Smashing Magazine網站的主頁上進行逐個像素的視覺測試,您會遇到很多失敗的測試,因為它們會產生越來越多的內容。

處理可視化測試的更好方法是利用 AI 等技術,每次運行測試時,測試框架都會智能地查看與基線相比捕獲的屏幕截圖。

它可以檢測到兩個捕獲是不同的,甚至可以檢測它是否是內容更改而不是佈局更改。 如果某些內容實際上沒有更改,它不會將該測試標記為失敗,您甚至可以添加規則以忽略可能由於該內容而更改的應用程序的動態區域。

視覺測試的幫助

可視化測試能夠以客戶看到的方式捕捉應用程序的當前狀態而蓬勃發展。 這使得它對於任何將有真人與之交互的應用程序都具有吸引力。

當它捕獲該快照時,它提供了該應用程序的許多部分的覆蓋範圍,而不僅僅是您為其編寫測試的單個粒度組件。 它最終捕獲了該組件周圍的上下文,從而擴大了覆蓋範圍。

這成為以低開銷提供廣泛覆蓋的好方法。 回到測試金字塔或測試獎杯,我們實際上能夠在所有其他測試之上提供全面的覆蓋。

視覺測試如何工作?

要點很簡單——將兩張圖像相互比較並尋找差異——但它比這更複雜一些。

圖像比較

在實施可視化測試時,目標是為關鍵工作流提供覆蓋,這些工作流可以捕捉到真人如何使用應用程序。 這通常包括某人可能看到的第一個屏幕,但這通常不是他們看到的唯一屏幕。

就像創建如何運行傳統測試的策略一樣,您要確保尋找最終在 UI 中產生真實結果的真實交互,例如將商品添加到購物車或將最喜歡的商品添加到您的願望清單。

考慮到這一點,您需要在每一步都截取屏幕截圖。 例如,如果您正在測試在線商店,您可能需要添加以下步驟:

  • 頁面上加載的產品列表;
  • 選擇單品後的產品頁面;
  • 將產品添加到購物車後的頁面購物車 UI;
  • 導航到購物車後的購物車頁面;
  • 進入結賬流程後的支付和發貨界面;
  • 購買成功後的收據頁面。

這將捕獲某人通過您的在線商店時的所有交互的結果,您可以在其中驗證它不僅從代碼的角度在功能上工作,而且該人實際上可以在沒有的情況下使用您的應用程序視覺錯誤干擾。

技術位

在規劃屏幕截圖時,您需要一種機制來自動執行這些任務。 可以基於一組任務與瀏覽器交互的東西。

這就是 Selenium、Cypress 和 Playwright 等流行的瀏覽器自動化框架的用武之地,這些測試框架利用瀏覽器 API 來運行您的命令,像人類一樣查找和單擊事物,然後您可以告訴可視化測試框架何時可視化捕獲 UI 狀態。

在 Cypress 和 Applitools 的情況下,Cypress 將瀏覽每個頁面,然後 Applitools SDK 將提取 DOM 的快照,並將該快照發送到 Applitools 雲,最終生成屏幕截圖進行比較。

顯示視覺測試如何與 Cypress 和 Applitools 配合使用的圖表
使用 Cypress 和 Applitools 進行可視化測試(大預覽)

那時,根據視覺測試平台,您將獲得一組結果,以突出顯示的差異或漂亮的綠色複選標記(如果看起來不錯)的形式。

與現有測試框架集成

就像上面的 Cypress 和 Applitools 集成一樣,集成通常具有低摩擦。 許多可用的可視化測試平台可以直接集成到現有的測試框架中,這主要取決於它們可用的 SDK。

 cy.visit('/product/1234'); cy.eyesOpen({ appName: 'Online Store', testName: 'Product Page' }); cy.eyesCheckWindow(); cy.eyesClose();

這意味著您通常不需要完全重寫您的測試套件來加強您的測試並獲得視覺覆蓋; 您可以將這些檢查點添加到您已有的測試中。

自動化測試

幸運的是,開發和測試相關任務的自動化已經迅速成熟,為我們如何自動化測試提供了很多很好的選擇。

傳統的 CI/CD 解決方案(如 Jenkins 或 Travis CI)允許您在集成和部署管道的其餘部分一起在他們的環境中運行測試。 相對較新的自動化領域是 GitHub Actions 等工具,它們提供與傳統 CI/CD 環境類似的機制,但就在您現有的 GitHub 存儲庫中。 這使得在嘗試自動運行測試和其他代碼任務時輕鬆獲勝,您不需要全新的系統,而是使用現有工具。

 name: Node.js CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: 12.x - run: npm ci - run: npm test

但是不管你使用什麼環境,最終你還是要服從測試框架的要求。 只要您可以訪問諸如 Electron 或 Chrome 之類的無頭瀏覽器,Cypress 就可以在您可以安裝 Node.js 的任何地方無縫運行,這在當今非常普遍。 其他人可能需要一些額外的環境,但此時,您通常可以根據需要搭建該環境,創建運行測試所需的條件。

視覺測試有什麼好處?

視覺測試將提供各種各樣的好處,就像我們已經討論過的一些好處一樣,但它確實可以幫助所有利益相關者,包括高管、產品經理、開發人員、設計師,以及團隊中的任何其他人。

例如,對於 CEO 或產品經理,您會確信您的測試覆蓋率實際上是在捕捉真實世界的使用情況。 對於開發團隊而言,您將獲得同樣的信心,即無論何時進行更改,您都會立即獲得反饋,從而消除了在嘗試快速行動時所涉及的恐懼因素。 但也有很多實際的好處。

需要維護的代碼更少

與可視化測試平台集成時,您的大部分代碼將圍繞兩件事展開:交互和屏幕截圖。

交互本質上是在應用程序中導航,找到您想要捕獲的頁面或用戶流。 無論您如何測試,您都可能需要以一種或另一種形式維護它。

另一方面,屏幕截圖將涵蓋您通常在測試中編寫的所有斷言。 通過將每個屏幕截圖與基線進行比較,您可以自動確保項目的每個組件都按預期工作。

測試不那麼脆弱

通過使用這些屏幕截圖作為您的斷言機制,您的測試將變得不那麼脆弱和脆弱。

如果您正在針對 DOM 的特定部分編寫斷言,例如使用 ID 或自動生成的選擇器,那麼您將冒著對該組件進行任何更改的破壞性測試的風險。

有了 ID,有人可能只是不小心刪除或更改了它。 也許您認為它僅用於功能目的並在返工時對其進行了更新,這最終破壞了測試(發生在我身上!)。

或者,如果您使用通用選擇器,無論是否自動生成,它們往往是非常具體的,因為您正在測試應用程序的非常特定的部分。 如果您最終在代碼中嵌套了一些 HTML 或稍微移動了一些東西,即使您沒有改變它的視覺外觀,它也可能最終破壞該測試。

測試人們實際使用的是什麼

說到測試它的視覺外觀,當視覺測試時,您正在測試您的訪問者或客戶實際看到的內容。

使用正確的語義 HTML 不會自動使您的項目可用。 一個小的 CSS 更改(如 z-index)可以完全改變可用性和外觀。

下拉菜單上帶有重疊按鈕的 Gmail 搜索
Gmail 中的視覺錯誤(大預覽)

通過捕獲屏幕截圖並通過用戶流程的交互來比較應用程序的實際狀態,您可以確保您的應用程序在功能上正常工作,並確保它不僅可供自動化機器人使用。

推薦閱讀在大型項目中管理 CSS Z-Index

測試你沒想過要測試的東西

您還可以覆蓋您甚至沒有想過要測試的應用程序的不同部分。

考慮一下你的套件中的測試列表,那些是你想寫或寫的,因為你之前遇到了一個錯誤。 應用程序的其餘部分呢?

該屏幕截圖將捕獲您的其他測試可能未包含的更多細節和上下文。

視覺測試不涵蓋什麼?

但是可視化測試並不是要替代整個測試套件的最終解決方案。 像其他類型的測試一樣,它應該共存,填補其​​他類型的空白並最終提供更有意義的覆蓋。

測試數據驅動的業務邏輯

當您進行可視化測試時,您可能能夠捕捉到業務邏輯的某些方面,例如確保有人將商品添加到購物車時,數學檢查出來了,但您的在線商店可能有更廣泛的種類不僅僅是那個產品。

通過單元測試捕獲複雜的業務邏輯仍然很重要,確保您捕獲不同的用例,例如各種折扣如何影響總數。

全面的 API 測試

在處理 API 時,您可以將可視化測試視為集成測試。 我們能夠測試在與瀏覽器交互時,我們的請求邏輯按預期工作。 但它沒有全面了解該 API 的運作方式。

與業務邏輯類似,您的 API 仍應由一組測試支持,以確保其按預期工作,例如單元測試或健康檢查。

視覺測試入門

作為我們腰帶中的另一個工具,可視化測試確實可以幫助提供另一個級別的覆蓋範圍,幫助我們為我們的應用程序保持高水平的質量,但是我們從哪裡開始呢?

融入開發生命週期

因為可視化測試有助於實現所有利益相關者的目標,所以它可以真正適用於開發生命週期的任何部分。

例如,傳統上測試僅用於驗證代碼是否按預期工作,但我們也可以將視覺測試用於設計交接和 UX 協作。 團隊中的設計師可以將他們的模型作為基線插入,並輕鬆使用它們來比較實際體驗。

但是從代碼的角度來看,可視化測試可以在自動化環境中蓬勃發展,例如對拉取請求運行檢查、部署前的暫存環境以及確保部署後的生產環境看起來不錯。

GitHub 拉取請求中的評論顯示視覺測試檢查
運行可視化測試的 GitHub Action(大預覽)

你甚至可以在 cron 上運行你的可視化測試,替換你的健康檢查合成事件,這些合成事件通常是不穩定的,並且不能真正告訴你你的應用程序處於什麼狀態。

幸運的是,您使用的服務以及使用這些服務的集成點都有很多選擇。

視覺測試的可用解決方案

確定要推進的解決方案取決於選擇您將用於運行測試的庫或服務。 就像我們之前提到的,最大的區別將是這些服務提供的視覺測試類型。

許多平台正在使用逐像素視覺測試來執行檢查。 這包括像 Percy 和 Chromatic 這樣的工具,它們將標記兩個屏幕截圖之間的變化。

然後你就有了人工智能驅動的視覺測試,Applitools 是目前唯一提供這種能力的服務。 Applitools 不是簡單地逐個像素地檢查圖像,而是智能地比較圖像,避免任何片狀測試或誤報,提供有意義的變化檢測。

無論解決方案如何,您最終都需要將其集成到您的開發環境中,無論您是從頭開始還是將其添加到現有的測試框架中。

集成視覺測試

在集成您選擇的可視化測試平台時,您可以選擇從頭開始,或者以更簡單的方式集成到您現有的測試框架中。 Applitools 之類的工具讓這一切變得簡單,其中支持的大量 SDK 有助於輕鬆融入現有工作流程。

一個很好的例子是,如果您已經設置並使用 Cypress 運行:

 it('should log into the application', () => { cy.get('#username').type('colbyfayock'); cy.get('#password').type('Password1234'); cy.get('#log-in').click(); cy.get('h1').contains('Dashboard'); });

如果您已經在執行程序化測試,您可以簡單地進行可視化測試以提供另一層覆蓋。

 it('should log into the application', () => { cy.eyesOpen({ appName: 'My App', testName: 'Login' }); cy.get('#username').type('colbyfayock'); cy.get('#password').type('Password1234'); cy.get('#log-in').click(); cy.eyesCheckWindow(); cy.eyesClose(); });

一些 SDK 讓它變得更加容易,如果您已經在運行 Storybook 庫,您需要做的就是使用 npm 安裝包並運行一個簡單的命令,然後您就可以完全覆蓋所有組件。

 npm install @applitools/eyes-storybook --save-dev npx eyes-storybook

最終,最大的問題是您要使用什麼測試框架,以及您要使用的服務是否支持該框架。

視覺測試的創造性使用

除了為您的應用程序獲得另一個級別的測試覆蓋率之外,您還可以通過多種其他方式來利用可視化測試。

  • 正常運行時間監控
    定期運行有意義的可視化測試,而不是使用脆弱的合成事件進行典型的正常運行時間監控。
  • 設計/用戶體驗協作
    從移交到可用性問題,使用可視化測試為整個團隊提供協作的媒介。
  • 可訪問性測試
    捕獲可能限制應用程序可訪問性的關鍵問題。
  • 歷史快照
    定期運行可視化測試可以幫助您捕獲快照,從而輕鬆地為您提供一種參考項目舊狀態的方法。
  • 本地化測試
    借助基於 AI 的視覺測試能夠檢測到內容變化,無論使用何種語言,您都可以確保一切正常並按預期工作。 獎勵:在嘗試比較給定語言的不同版本時,您可以減少開銷。

通過將視覺元素添加到您的測試中,您將獲得更多選項來添加有意義的方式來保持應用程序的高質量。