연습 - 로컬 및 파이프라인에서 UI 테스트 실행

완료됨

Andy와 Amita가 파이프라인에서 테스트를 실행하기 전에 새 UI 테스트가 수행해야 할 작업을 수행하는지 확인하려고 합니다. 이 섹션에서는 Selenium UI 테스트를 먼저 로컬로 실행한 다음 파이프라인에서 실행하는 순서로 진행합니다.

자동화된 테스트 작성은 다른 형식의 코드를 작성하는 것과 마찬가지로 반복적인 프로세스입니다. 사용자 고유 앱에 대해 몇 가지 방법을 시도해 보고, 참조 설명서와 예제 코드를 참조하고, 빌드 오류를 수정해야 할 수 있습니다.

선택 사항: Microsoft Edge용 Selenium 드라이버 설치

Microsoft Edge에서 테스트가 로컬로 실행되는 것을 확인하려는 경우 이 섹션을 따릅니다.

Chrome 및 Firefox용 NuGet 패키지는 컴파일된 테스트 코드와 함께 bin 디렉터리에 드라이버 소프트웨어를 설치합니다. Edge의 경우 드라이버를 수동으로 설치해야 합니다. 이를 수행하려면:

  1. Microsoft Edge를 설치합니다.

  2. Edge를 열고 edge://settings/help로 이동합니다. 버전 번호를 메모합니다. 예를 들면 다음과 같습니다.

    A screenshot of the Microsoft Edge setting page, showing the version number.

  3. Microsoft Edge 드라이버 다운로드 페이지로 이동하여 Edge 버전 번호와 일치하는 드라이버를 다운로드합니다. 예를 들면 다음과 같습니다.

    A screenshot of the Downloads section of the Microsoft Edge Driver page showing the matching driver version.

  4. 프로젝트의 Tailspin.SpaceGame.Web.UITests 디렉터리 아래 bin/Release/net6.0 디렉터리에 .zip 파일의 압축을 풉니다. 이러한 디렉터리가 없으면 만듭니다.

macOS에서는 msedgedriver 실행을 허용하도록 시스템 정책을 업데이트해야 할 수 있습니다. 이렇게 하려면 Visual Studio Code의 터미널에서 다음 spctl 명령을 실행합니다.

spctl --add Tailspin.SpaceGame.Web.UITests/bin/Release/net6.0/msedgedriver

환경 변수 내보내기

이 모듈의 뒷부분에서는 Windows Server 2019에서 Selenium 테스트를 실행합니다. 설명서에는 사전 설치된 소프트웨어가 나열되어 있습니다.

Selenium 웹 드라이버 섹션에는 Chrome, Firefox, Edge에 사용할 수 있는 Selenium 드라이버 버전이 나열되어 있습니다. 예를 들면 다음과 같습니다.

A screenshot showing the documentation for the installed Selenium drivers on the build agent.

드라이버마다 해당 드라이버의 위치에 매핑되는 환경 변수가 있습니다. 예를 들어는 ChromeWebDriver는 Chrome 드라이버의 위치에 매핑됩니다.

단위 테스트 코드는 해당 환경 변수를 읽도록 이미 설정되어 있습니다. 해당 변수는 Selenium에 드라이버 실행 파일을 찾을 수 있는 위치를 알려줍니다. 단위 테스트를 로컬로 실행하려면 동일한 환경 변수를 내보내야 합니다.

Visual Studio Code에서 터미널로 이동합니다. 그런 후에 다음 명령을 실행합니다. 표시된 경로는 다음과 같이 mslearn-tailspin-spacegame-web-deploy 프로젝트의 전체 경로로 바꿉니다.

중요

이러한 명령을 실행하고 테스트를 실행하는 데 사용하는 것과 동일한 터미널 창에서 환경 변수를 설정해야 합니다.

driverDir="C:\Users\user\mslearn-tailspin-spacegame-web-deploy\Tailspin.SpaceGame.Web.UITests\bin\Release\net6.0"
export ChromeWebDriver=$driverDir
export EdgeWebDriver=$driverDir
export GeckoWebDriver=$driverDir

로컬로 UI 테스트 실행

HomePageTest.csSetup 메서드는 driver 멤버 변수를 설정한 후 Space Game 홈페이지로 이동합니다.

사이트 URL을 하드 코딩할 수도 있지만 여기서는 SITE_URL이라는 환경 변수에서 URL을 읽습니다. 이러한 방식으로 여러 URL에 대해 테스트를 여러 번 실행할 수 있습니다.

// Navigate to the site.
// The site name is stored in the SITE_URL environment variable to make 
// the tests more flexible.
string url = Environment.GetEnvironmentVariable("SITE_URL");
driver.Navigate().GoToUrl(url + "/");

App Service 환경에 Space Game 웹 사이트를 아직 배포하지 않았으므로 Microsoft에서 호스트하는 사이트를 사용하여 테스트를 로컬로 실행합니다.

로컬로 테스트를 실행하려면 다음을 수행합니다.

  1. Visual Studio Code에서 통합 터미널로 이동하여 새 터미널 창을 엽니다.

  2. 새 터미널 창에서 다음 명령을 실행합니다.

    dotnet build --configuration Release
    dotnet run --configuration Release --no-build --project Tailspin.SpaceGame.Web
    
  3. 로컬 웹 사이트 링크를 기록해 두세요. 이 예제에서는 http://localhost:5000입니다.

  4. 이전 단계에서 환경 변수를 설정한 터미널 창으로 다시 전환하고 프로젝트의 루트 디렉터리에 있는지 확인합니다. 예를 들면 다음과 같습니다.

    cd ~/mslearn-tailspin-spacegame-web-deploy
    
  5. SITE_URL 환경 변수를 내보냅니다. 이전 단계에서 가져온 로컬로 실행 중인 링크를 사용합니다.

    export SITE_URL="http://localhost:5000"
    

    이 변수는 Microsoft에서 호스트하는 Space Game 웹 사이트를 가리킵니다.

  6. UI 테스트를 실행합니다.

    dotnet test --configuration Release Tailspin.SpaceGame.Web.UITests
    

    위 코드는 Tailspin.SpaceGame.Web.UITests 프로젝트에 있는 테스트를 실행합니다.

    테스트가 실행되면 브라우저가 하나 이상 나타납니다. Selenium은 각 브라우저를 제어하고 개발자가 정의한 테스트 단계를 따릅니다.

    참고

    브라우저 3개 중 일부가 표시되지 않더라도 걱정할 필요가 없습니다. 예를 들어 Chrome이 설치되어 있지 않거나 호환되지 않는 버전이 있는 경우 Chrome에서 테스트가 실행되지 않습니다. 브라우저 하나에서만 테스트가 실행되는 것을 확인하면 테스트가 작동한다는 확신을 가질 수 있습니다. 실제로 로컬 개발 환경에서는 테스트하려는 모든 브라우저를 설정할 수 있습니다. 이 설정을 통해 파이프라인에서 테스트를 실행하기 전에 각 구성에서 테스트가 예상대로 동작하는지 확인할 수 있습니다.

  7. 터미널에서 각 테스트의 출력을 추적합니다. 또한 끝에서 테스트 실행 요약을 확인합니다.

    이 예제에서는 9개의 테스트 중에서 9개의 테스트가 모두 성공했으며 0개의 테스트를 건너뛰는 것을 보여 줍니다.

    Passed!  - Failed:     0, Passed:     9, Skipped:     0, Total:     9, Duration: 5 s 
    

Azure Pipelines에 SITE_URL 변수 추가

이전에는 테스트에서 각 브라우저를 가리킬 위치를 알 수 있도록 SITE_URL 환경 변수를 로컬로 설정했습니다. 해당 변수를 Azure Pipelines에 추가할 수 있습니다. 이 프로세스는 App Service 인스턴스에 변수를 추가하는 방법과 비슷합니다. 에이전트가 실행될 때 해당 변수는 환경 변수로 에이전트에 자동으로 내보내집니다.

파이프라인 구성을 업데이트하기 전에 파이프라인 변수를 추가해 보겠습니다. 이를 수행하려면:

  1. Azure DevOps에서 Space Game - web - Functional tests 프로젝트로 이동합니다.

  2. Pipelines에서 Library를 선택합니다.

  3. 릴리스 변수 그룹을 선택합니다.

  4. Variables에서 + Add를 선택합니다.

  5. 변수 이름으로 SITE_URL을 입력합니다. 테스트 환경에 해당하는 App Service 인스턴스의 URL(예: http://tailspin-space-game-web-test-10529.azurewebsites.net)을 값으로 입력합니다.

  6. 페이지 상단에서 저장을 선택하여 변수를 파이프라인에 저장합니다.

    변수 그룹은 다음과 비슷합니다.

    A screenshot of Azure Pipelines, showing the variable group. The group contains four variables.

파이프라인 구성 수정

이 섹션에서는 ‘테스트’ 단계 중에 Selenium UI 테스트를 실행하도록 파이프라인 구성을 수정합니다.

  1. Visual Studio Code에서 azure-pipelines.yml 파일을 엽니다. 그런 파일을 다음과 같이 수정합니다.

    이 파일에는 몇 가지 변경 내용이 포함되어 있음으로 전체 파일을 여기에 표시된 내용으로 바꾸는 것이 좋습니다.

    trigger:
    - '*'
    
    variables:
      buildConfiguration: 'Release'
      dotnetSdkVersion: '6.x'
    
    stages:
    - stage: 'Build'
      displayName: 'Build the web application'
      jobs: 
      - job: 'Build'
        displayName: 'Build job'
        pool:
          vmImage: 'ubuntu-20.04'
          demands:
          - npm
    
        variables:
          wwwrootDir: 'Tailspin.SpaceGame.Web/wwwroot'
          dotnetSdkVersion: '6.x'
    
        steps:
        - task: UseDotNet@2
          displayName: 'Use .NET SDK $(dotnetSdkVersion)'
          inputs:
            version: '$(dotnetSdkVersion)'
    
        - task: Npm@1
          displayName: 'Run npm install'
          inputs:
            verbose: false
    
        - script: './node_modules/.bin/node-sass $(wwwrootDir) --output $(wwwrootDir)'
          displayName: 'Compile Sass assets'
    
        - task: gulp@1
          displayName: 'Run gulp tasks'
    
        - script: 'echo "$(Build.DefinitionName), $(Build.BuildId), $(Build.BuildNumber)" > buildinfo.txt'
          displayName: 'Write build info'
          workingDirectory: $(wwwrootDir)
    
        - task: DotNetCoreCLI@2
          displayName: 'Restore project dependencies'
          inputs:
            command: 'restore'
            projects: '**/*.csproj'
    
        - task: DotNetCoreCLI@2
          displayName: 'Build the project - $(buildConfiguration)'
          inputs:
            command: 'build'
            arguments: '--no-restore --configuration $(buildConfiguration)'
            projects: '**/*.csproj'
    
        - task: DotNetCoreCLI@2
          displayName: 'Publish the project - $(buildConfiguration)'
          inputs:
            command: 'publish'
            projects: '$(System.DefaultWorkingDirectory)/**/Tailspin.SpaceGame.Web.csproj' 
            publishWebProjects: false
            arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
            zipAfterPublish: true
    
        - publish: '$(Build.ArtifactStagingDirectory)'
          artifact: drop
    
    - stage: 'Dev'
      displayName: 'Deploy to the dev environment'
      dependsOn: Build
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: dev
        variables:
        - group: Release
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameDev)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
    
    - stage: 'Test'
      displayName: 'Deploy to the test environment'
      dependsOn: Dev
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: test
        variables:
        - group: 'Release'
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameTest)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
      - job: RunUITests
        dependsOn: Deploy
        displayName: 'Run UI tests'
        pool:
          vmImage: 'windows-2019'
        variables:
        - group: 'Release'
        steps: 
        - task: UseDotNet@2
          displayName: 'Use .NET SDK $(dotnetSdkVersion)'
          inputs:
            version: '$(dotnetSdkVersion)'
        - task: DotNetCoreCLI@2
          displayName: 'Build the project - $(buildConfiguration)'
          inputs:
            command: 'build'
            arguments: '--configuration $(buildConfiguration)'
            projects: '$(System.DefaultWorkingDirectory)/**/*UITests.csproj'
        - task: DotNetCoreCLI@2
          displayName: 'Run unit tests - $(buildConfiguration)'
          inputs:
            command: 'test'
            arguments: '--no-build --configuration $(buildConfiguration)'
            publishTestResults: true
            projects: '$(System.DefaultWorkingDirectory)/**/*UITests.csproj'
    
    - stage: 'Staging'
      displayName: 'Deploy to the staging environment'
      dependsOn: Test
      jobs:
      - deployment: Deploy
        pool:
          vmImage: 'ubuntu-20.04'
        environment: staging
        variables:
        - group: 'Release'
        strategy:
          runOnce:
            deploy:
              steps:
              - download: current
                artifact: drop
              - task: AzureWebApp@1
                displayName: 'Azure App Service Deploy: website'
                inputs:
                  azureSubscription: 'Resource Manager - Tailspin - Space Game'
                  appName: '$(WebAppNameStaging)'
                  package: '$(Pipeline.Workspace)/drop/$(buildConfiguration)/*.zip'
    

    이 파일에는 다음과 같은 세 가지 변경 내용이 포함됩니다.

    • dotnetSdkVersion 변수는 여러 단계에서 액세스할 수 있도록 파일의 맨 위로 이동합니다. 여기에서 ‘빌드’ 단계와 ‘테스트’ 단계에는 이 버전의 .NET Core가 필요합니다.

    • ‘빌드’ 단계에서는 Space Game 웹 사이트 패키지만 빌드 아티팩트로 게시합니다. 이전에는 다음과 같은 아티팩트를 게시했습니다.

      - task: DotNetCoreCLI@2
        displayName: 'Publish the project - $(buildConfiguration)'
        inputs:
          command: 'publish'
          projects: '**/*.csproj'
          publishWebProjects: false
          arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
          zipAfterPublish: true
      

      이 작업은 Space Game 웹 사이트 패키지와 컴파일된 UI 테스트라는 두 개의 빌드 아티팩트를 생성합니다. 빌드 단계 중에 UI 테스트를 빌드하여 테스트 단계 중에 컴파일되도록 하지만 컴파일된 테스트 코드를 게시할 필요는 없습니다. 테스트를 실행할 때 ‘테스트’ 단계 중에 이를 다시 빌드합니다.

    • ‘테스트’ 단계에는 테스트를 빌드하고 실행하는 두 번째 작업이 포함되어 있습니다. 이 작업은 Azure Pipelines를 사용하여 빌드 파이프라인에서 품질 테스트 실행 모듈에서 사용한 작업과 유사합니다. 이 모듈에서는 순위표의 필터링 기능을 확인하는 NUnit 테스트를 실행했습니다.

      ‘배포 작업’은 배포 단계에서 중요한 역할을 수행하는 특별한 유형의 작업입니다. 두 번째 작업은 Windows Server 2019 에이전트에서 Selenium 테스트를 실행하는 일반적인 작업입니다. Linux 에이전트를 사용하여 애플리케이션을 빌드했지만 여기서는 Windows 에이전트를 사용하여 UI 테스트를 실행합니다. Amita가 Windows에서 수동 테스트를 실행했고 대부분의 고객이 사용하는 운영 체제이기 때문에 Windows 에이전트를 사용합니다.

      RunUITests 작업은 Deploy 작업을 사용하여 작업이 올바른 순서로 실행되는지 확인합니다. UI 테스트를 실행하기 전에 App Service에 웹 사이트를 배포합니다. 이 종속성을 지정하지 않으면 단계 내의 작업이 임의의 순서로 실행되거나 병렬로 실행될 수 있습니다.

  2. 통합 터미널에서 azure-pipelines.yml을 인덱스에 추가하고, 변경 내용을 커밋하며, 분기를 GitHub로 푸시합니다.

    git add azure-pipelines.yml
    git commit -m "Run Selenium UI tests"
    git push origin selenium
    

테스트를 실행하는 Azure Pipelines 보기

여기서 파이프라인 실행을 확인합니다. 파이프라인은 ‘테스트’ 단계 중에 Selenium UI 테스트를 실행합니다.

  1. Azure Pipelines에서 빌드로 이동하고 실행되는 빌드를 추적합니다.

    빌드 중에는 웹 사이트를 배포한 후 실행되는 자동화된 테스트를 볼 수 있습니다.

    A screenshot of Azure Pipelines, showing the running stages.

  2. 빌드가 완료되면 요약 페이지로 이동합니다.

    A screenshot of Azure Pipelines, showing the completed stages.

    배포 및 UI 테스트가 성공적으로 완료된 것을 확인합니다.

  3. 페이지 상단 부근에서 요약을 확인합니다.

    Space Game 웹 사이트의 빌드 아티팩트가 언제나처럼 게시된 것을 확인합니다. Selenium 테스트가 통과되었음을 보여 주는 테스트 및 검사 섹션도 확인합니다.

    A screenshot of Azure Pipelines, showing the test summary.

  4. 테스트 요약을 선택하여 전체 보고서를 확인합니다.

    보고서에 9개 테스트가 모두 통과된 것으로 나타납니다. 해당 테스트에는 3개 브라우저에서 실행된 3개 테스트가 포함됩니다.

    A screenshot of Azure Pipelines, showing the full test report.

    테스트에 실패하면 실패에 대한 자세한 결과를 볼 수 있습니다. 여기에서 실패 원인을 조사하고, 로컬에서 수정한 다음 파이프라인에서 테스트가 통과하도록 필요한 변경 내용을 푸시할 수 있습니다.

Amita: 이 자동화는 아주 흥미로워요! 이제 파이프라인에서 실행할 수 있는 UI 테스트가 있어요. 해당 테스트 덕분에 장기적으로 시간이 절약될 거예요. 그리고 테스트를 더 추가하기 위해 따라야 하는 패턴이 있어요. 무엇보다도 UI 테스트를 통해 코드 품질을 더욱 확신할 수 있게 되었어요.

Andy: 모두 맞는 말씀이네요. Amita 님이 반복해서 수행하는 테스트는 자동화의 좋은 후보죠. 테스트를 잘 추가해 보세요. 하다가 막히거나 코드 검토자가 필요하면 언제든지 연락주세요.