练习 - 在 Azure Pipelines 中运行负载测试

已完成

在本部分中,你将运行在发布管道中创建的测试计划。 测试计划使用 Apache JMeter 运行负载测试。

下面是运行测试的方式:

  • 提取并查看实现测试的 Git 分支。
  • 修改管道以安装 JMeter、运行测试计划、将结果转换为 JUnit,并将结果发布到 Azure Pipelines。
  • 将分支推送到 GitHub,观察在 Azure Pipelines 中运行的测试,然后检查结果。

从 GitHub 中提取分支

在本部分中,你将从 GitHub 提取 jmeter 分支,签出或切换到该分支。

该分支包含在之前的模块中使用过的 Space Game 项目。 它还包含要从其开始的 Azure Pipelines 配置。

  1. 在 Visual Studio Code 中打开集成终端。

  2. 要从 Microsoft 的存储库中下载名为 jmeter 的分支,并切换到该分支,请运行以下 git fetchgit checkout 命令:

    git fetch upstream jmeter
    git checkout -B jmeter upstream/jmeter
    

    回想一下,“upstream”指的是 Microsoft GitHub 存储库。 项目的 Git 配置能够识别上游远程库,因为当你从 Microsoft 的存储库中创建该项目的分支并在本地克隆它时,就建立了这种关系。

    稍后,你会将此分支推送到 GitHub 存储库(称作 origin)。

  3. (可选)在 Visual Studio Code 中,打开 azure-pipelines.yml 文件。 查看初始配置。

    该配置类似于在此学习路径的上一个模块中创建的配置。 它仅生成应用程序的 发布 配置。 为简洁起见,它省略了在以前的模块中设置的触发器、手动审批和测试。

    注释

    可以使用更可靠的配置来指定参与生成过程的分支。 例如,为了帮助验证代码质量,你可以在每次对任何分支推送更改时运行单元测试。 你还可以将应用程序部署到执行更全面测试的环境。 但仅当你有拉取请求、候选发布或将代码合并到主分支时,才能执行此部署。

    有关详细信息,请参阅在构建流水线中使用 Git 和 GitHub 实现代码工作流构建流水线触发器

  4. (可选)在 Visual Studio Code 中,可以查看 JMeter 测试计划文件 、LoadTest.jmx 和 XLST 转换 JMeter2JUnit.xsl。 XLST 文件将 JMeter 输出转换为 JUnit,以便 Azure Pipelines 能够直观显示结果。

将变量添加到 Azure Pipelines

团队的原始测试计划为在暂存环境中运行的Space Game网站的主机名提供硬编码值。

要使测试计划更加灵活,您的版本使用了 JMeter 属性。 将属性视为可从命令行设置的变量。

在 JMeter 中,变量 hostname 是这样定义的:

在 Apache JMeter 中设置主机名变量的屏幕截图。

下面是变量如何使用 hostname__P 函数读取变量 hostname

在 Apache JMeter 中读取主机名变量的屏幕截图。

相应的测试计划文件 LoadTest.jmx 指定此变量,并使用它来设置主机名。

从命令行运行 JMeter 时,可以使用 -J 参数设置 hostname 属性。 下面是一个示例:

apache-jmeter-5.4.3/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=tailspin-space-game-web-staging-1234.azurewebsites.net

在这里,在 Azure Pipelines 中设置 STAGING_HOSTNAME 变量。 此变量指向在 暂存 环境中的应用服务上运行的站点主机名。 还可以设置 jmeterVersion 指定要安装的 JMeter 版本。

代理运行时,这些变量会自动导出到代理作为环境变量,因此管道配置可以按以下方式运行 JMeter:

apache-jmeter-5.4.3/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=$(STAGING_HOSTNAME)

现在,在更新管道配置之前,让我们添加管道变量。 为此,做以下事情:

  1. 在 Azure DevOps 中,转到 Space Game - Web - 非功能测试 项目。

  2. 在“Pipelines”下,选择“Library”。

  3. 选择“发布”变量组。

  4. 在“变量”下,选择“+添加”。

  5. 对于变量的名称,请输入 STAGING_HOSTNAME。 对于其值,请输入与 暂存 环境相对应的应用服务实例的 URL,例如 tailspin-space-game-web-staging-1234.azurewebsites.net

    重要

    不要在值中包含http://https://协议前缀。 JMeter 在测试运行时提供协议。

  6. 添加名为 jmeterVersion 的第二个变量。 对于其值,请指定 5.4.3

    注释

    这是我们上次用于测试此模块的 JMeter 版本。 若要获取最新版本,请参阅 下载 Apache JMeter

  7. 若要将变量保存到管道,请选择页面顶部附近的 “保存 ”。

    变量组类似于下图所示的变量组:

    Azure Pipelines 的屏幕截图,其中显示了变量组。该组包含五个变量。

修改管道配置

在本部分中,你将修改管道,以在 过渡 阶段运行负载测试。

  1. 在 Visual Studio Code 中,打开 azure-pipelines.yml 文件。 然后修改文件,如下所示:

    小窍门

    可以替换整个文件,也可以只更新突出显示的部分。

    trigger:
    - '*'
    
    variables:
      buildConfiguration: 'Release'
    
    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: '**/*.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'
    
    - 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'
      - job: RunLoadTests
        dependsOn: Deploy
        displayName: 'Run load tests'
        pool:
          vmImage: 'ubuntu-20.04'
        variables:
        - group: Release
        steps:
        - script: |
            wget -c archive.apache.org/dist/jmeter/binaries/apache-jmeter-$(jmeterVersion).tgz
            tar -xzf apache-jmeter-$(jmeterVersion).tgz
          displayName: 'Install Apache JMeter'
        - script: apache-jmeter-$(jmeterVersion)/bin/./jmeter -n -t LoadTest.jmx -o Results.xml -Jhostname=$(STAGING_HOSTNAME)
          displayName: 'Run Load tests'
        - script: |
            sudo apt-get update
            sudo apt-get install xsltproc
            xsltproc JMeter2JUnit.xsl Results.xml > JUnit.xml
          displayName: 'Transform JMeter output to JUnit'
        - task: PublishTestResults@2
          inputs:
            testResultsFormat: JUnit
            testResultsFiles: JUnit.xml
    

    下面是更改的摘要:

    • RunLoadTests 作业从 Linux 代理执行负载测试。
    • 作业 RunLoadTests 取决于 Deploy 作业,以确保作业按正确的顺序运行。 需要先将网站部署到应用服务,然后才能运行负载测试。 如果未指定此依赖性,则阶段中的作业可以按任何顺序运行,也可以并行运行。
    • 第一个 script 任务下载并安装 JMeter。 管道 jmeterVersion 变量指定要安装的 JMeter 版本。
    • 第二个 script 任务运行 JMeter。 该-J参数通过从管道中读取STAGING_HOSTNAME变量来设置 JMeter 中的hostname属性。
    • 第三 script 个任务安装 xsltproc、XSLT 处理器,并将 JMeter 输出转换为 JUnit。
    • PublishTestResults@2 任务将生成的 JUnit 报表 JUnit.xml发布到管道。 Azure Pipelines 可帮助你直观显示测试结果。
  2. 在集成终端中,将 azure-pipelines.yml 添加到索引,提交更改,然后将分支推送到 GitHub。

    git add azure-pipelines.yml
    git commit -m "Run load tests with Apache JMeter"
    git push origin jmeter
    

观察 Azure Pipelines 运行测试

现在,你将观察管道运行。 你将看到负载测试在 预发布期间运行。

  1. 在 Azure Pipelines 中,转到生成并在运行时对其进行跟踪。

    预发布期间,可以看到在部署网站后运行负载测试。

  2. 生成完成后,转到摘要页。

    Azure Pipelines 的屏幕截图,其中显示了已完成的阶段。

    可以看到部署和负载测试成功完成。

  3. 在页面顶部附近,注意摘要。

    你会看到 Space Game 网站的生成项目像往常一样发布。 另请注意 “测试和覆盖率 ”部分,其中显示了负载测试已通过。

    Azure Pipelines 的屏幕截图,其中显示了测试摘要。

  4. 选择“测试摘要”以查看完整的报告。

    报告显示这两个测试都已通过。

    Azure Pipelines 的屏幕截图,其中显示了完整的测试报告。

    如果测试失败,则会看到失败的详细结果。 从这些结果中,可以调查故障的来源。

    回想一下,XSLT 文件生成名为 JUnit.xml的 JUnit 文件。 JUnit 文件回答了以下两个问题:

    • 平均请求时间是否小于 1 秒?
    • 是否少于 10% 的请求需要超过 1 秒才能完成?

    报告证明满足这些要求。 若要查看更多详细信息,请选择报表中的 “结果 ”箭头。 然后确保仅选中“通过”

    测试报告中筛选通过测试的屏幕截图。

    你会看到 平均响应时间最大响应时间 测试用例都成功。

    测试报告的屏幕截图,其中显示了两个成功的测试用例。

注释

你使用的是在基本层上运行的 B1 应用服务计划。 此计划适用于流量要求较低的应用,例如测试环境中的应用。 由于此计划,网站的性能可能低于预期。 实际操作时,可以选择一个与生产环境更匹配的适用于“过渡”环境的计划。 例如,标准和高级计划适用于生产工作负荷。 这些实例在专用虚拟机实例上运行。