標籤: pipeline

Playwright在Azure Pipeline配置

Playwright在Azure Pipeline配置

2024年10月2日,颱風天雖然無風無雨,但還是放了颱風假,只好待在家裡研究最近上保哥的 Playwright 課程後的一些心得和小成果。

從在本地環境使用 Node.js 和 TypeScript 建立測試案例到執行,已經有很多文件和參考資料,所以這部分就先略過。

不過對於自動化框架已經有這麼多套了後,為什麼目前選擇playwright呢?上完課初步瞭解後,我自己整理有以下幾點

  1. playwright簡化了selector,重新設計了定位器,符合無障礙網頁設計規範,與autowait,並可以透過 playwright生態與Api支援下 ,測試可讀性也大幅增加,配合強大工具進行錄製,大幅降低了e2e測試案例寫code的困難度,也降低了維護上的成本。
  2. 強大的Mocking的機制,還有諸多黑科技機制 ,不旦可以mocking api , browser 的storage, 有機會達成更多深入的驗證情境(例如pass 人類、簡訊等雙因子驗證)
  3. 支持所有顯示主流語言,包含java , c# , nodejsd的type script,且可以透過 框架一鍵切換,文件也很完整。
  4. 微軟爸爸金主萬歲

其實,E2E的測試,還有一個就是文化面的觀點,就是所有的測試都必須跟RD雙向奔赴,一面測試能夠做到反饋,產品主動優化,這樣正向循環才有助於產品品質的發展。

作為一個自動化測試框架,通常後續整合到專案或產品上有兩個實作方向:

1. 作為前端專案的介面整合測試

在這種情況下,測試案例的版控權限需要開放到前端專案中。因此,如果開發人員以外的人(如 QA、PM)要協助編修測試案例,可能會頻繁變動專案。而且可能還需要在建置過程中處理 localhost 的啟動及套件測試驗證。這樣的測試其實有點像界面的單元測試,只是更依賴於前端專案,並且還要考慮相關的服務或其他環境的設定。

當然這種方式的好處在於,測試流程可以隨著介面的變更一起調整,與前端工程師的工作息息相關。因此,測試案例的防守範圍包含了 RD,可以隨著功能特性(feature)的調整,達到與版本部署的同步。

但維護成本也相對高(跟專案綁在一起的關係)

2. 作為產品介面的回歸測試

這種方式著重於將較為穩定且不常變動的介面優先建置測試案例。目前,我們專案正朝著這樣的 E2E 測試流程發展,希望能針對不同環境(如 tst、uat、preprod、prod)進行測試。藉由Azure Pileine配置獨立的 E2E 測試專案及執行指令,可以靈活地測試不同環境的標的。

獨立了 test 專案後,測試team(當然也可以RD、PM都一起來寫XD)可以依據不同環境規劃測試案例的目錄。透過 npx playwright test 指令,可以方便地測試指定的案例目錄及不同瀏覽器參數。這種方式相當便捷(先前也玩過 Cypress),並且獨立版控的測試專案也帶來了一些好處,例如可以在 CI 流程中靈活分配,無論是每次上版到不同環境時進行或是定期測試、在指定條件下執行測試,都能自由安排。

這次我希望達到的是上述第二種切入點的做法,因為我們的 E2E 測試希望從使用者操作的角度進行,以 “指定環境” 的 Web 站台為測試對象。

因此我們將BASE_URL(測試站台的進入點)作為 參數由外部傳入

另外,因為考慮了playwright的覆用性,因此也把playwright的大部分Task包成另一個 Azure Template

這樣一來,不同環境就可以建置不同的Pipeline去定義 參數後再傳入PipelineTemplate執行標準的測試 Task

Main Pipeline

(以下範例移除了公司的標記,可以依有抽出參數的部分自由參考運用)


pool:
  vmImage: ubuntu-latest

trigger:
  branches:
    include:
      - 'main'

resources:
  repositories:
  - repository: PipelineTemplates
    type: git
    name: {版控}/Pipeline-Templates
  - repository: E2ETesting
    type: git
    name: {專案}-End2EndTesting

name: $(Build.BuildId)

stages:

  - stage: qa
    displayName: 'Run Automation Test - QA'
    dependsOn: []
    jobs:
      - template: template/playwright-template.yml@PipelineTemplates
        parameters:
          BASE_URL: 'https://tst-{站台}/zh-TW/home' #測試站點
          End2EndProjectKey: E2ETesting  #測試專案版控Key
          End2EndProjectName: {專案}-End2EndTesting #測試專案目錄名稱(用於組合路徑用)

PipelineTemplates

(作為Function可以被不同的pipeline參考執行,注意,這個是 playwright-template.yml的內容,另外一個檔案)


parameters:
  - name: BASE_URL
    type: string
  - name: End2EndProjectKey
    type: string
  - name: End2EndProjectName
    type: string

jobs:

  - job: test
    displayName: Run Playwright Tests
    steps:
      - download: none
      - checkout: self
        persistCredentials: true    
      - checkout: ${{ parameters.End2EndProjectKey }}
        persistCredentials: true

      - script: |
          ls $(System.DefaultWorkingDirectory)
        displayName: "Print Working Dir"

      - task: NodeTool@0
        displayName: 'Use Node version 18'
        inputs:
          versionSpec: 18.x

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
        displayName: "Cd E2E Folder and ls folder "

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
          npm ci
        displayName: "NPM Install"

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
          pwd
          echo $(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/browsers
          cd /home/vsts/
          PLAYWRIGHT_BROWSERS_PATH=/home/vsts/
          npx playwright install --with-deps
           #note 不要用sudo安裝,會裝到/root去專案跑的時候會找不到browser driver
        displayName: "Playwright Install"

      - script: |
          echo "BASE_URL= ${{ parameters.BASE_URL }}"
        displayName: "Print Variables"

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          BASE_URL=${{ parameters.BASE_URL }} CI=true PLAYWRIGHT_JUNIT_OUTPUT_NAME=test-results/results.xml npx playwright test
        displayName: "Run Playwright Tests"
        continueOnError: true

      - task: ArchiveFiles@2
        displayName: 'Add playwright-report to Archive'
        inputs:
          rootFolderOrFile: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/playwright-report/'
          archiveFile: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'

      - task: ArchiveFiles@2
        displayName: 'Add test-results to Archive'
        inputs:
          rootFolderOrFile: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/test-results/'
          archiveFile: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'
          replaceExistingArchive: false

      - task: PublishPipelineArtifact@1
        displayName: 'Publish Pipeline Artifacts'
        inputs:
          targetPath: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'
          artifact: pipeline-artifacts

      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'JUnit'
          testResultsFiles: '*.xml'
          searchFolder: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/test-results'
          testRunTitle: 'Playwright ADO Demo - $(System.StageName)'
        displayName: 'Publish Test Results'

成果