Help us learn about your current experience with the documentation. Take the survey.

端到端测试编写入门指南

本教程将指导您为 GitLab Community EditionGitLab Enterprise Edition 创建端到端(e2e)测试。

完成本教程后,您将能够:

  • 判断是否需要编写端到端测试。
  • 理解 qa/ 目录下的目录结构。
  • 编写验证登录功能的基本端到端测试。
  • 开发任何缺失的 page object 库。

编写测试前

在编写测试之前,您的 GitLab Development Kit (GDK) 必须配置为运行测试规范。端到端测试:

  • 包含在 qa/ 目录中。
  • 应该是独立的和 幂等的
  • 按需创建 资源(如项目、问题、用户)。
  • 测试 UI 和 API 接口,并使用 API 来高效设置 UI 测试。

确定是否需要端到端测试

在为 GitLab 项目编写端到端测试之前,请检查特定功能的代码覆盖率。在单元、功能或集成级别是否有足够的测试覆盖率?如果答案是 ,那么您 不需要 端到端测试。

有关 GitLab 中各测试级别分布的信息,请参阅 Testing Levels

  • 查看 Testing levels 文档中的 How to test at the correct level? 部分。
  • 评估功能变更的频率。如果功能已经由较低级别的测试覆盖,且变更不频繁,那么可能不值得使用端到端测试来覆盖。
  • 最后,与实现该功能和编写较低级别测试的开发人员讨论建议的测试方案。

检查 GitLab 覆盖率项目中是否已有针对此功能的测试。要分析代码覆盖率,您必须了解哪些应用程序文件实现了特定功能。

在本教程中,我们将编写登录端到端测试,尽管它已被较低级别的测试充分覆盖,因为它是大多数端到端流程的第一步,且最容易理解。

识别 DevOps 阶段

GitLab QA 端到端测试按照 DevOps 生命周期中的不同阶段 组织。通过 stage 确定测试应放置的位置,确定测试所属的功能,然后将其放置在阶段下的子目录中。

DevOps lifecycle by stages

如果测试仅适用于企业版,则测试创建在 features/ee 目录中,但遵循相同的 DevOps 生命周期格式。

创建骨架测试

在本教程的第一部分,我们测试登录功能,它属于管理阶段。在 qa/specs/features/browser_ui/1_manage/login 目录下,创建一个 basic_login_spec.rb 文件。

外部 context

参见 RSpec.describe 外部块

外部 context 已被弃用,以遵循 RSpec 4.0 规范。请使用 RSpec.describe 替代。

外部 RSpec.describe

测试规范有一个外部的 RSpec.describe,表示 DevOps 阶段。

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do

  end
end

describe

在我们的外部 RSpec.describe 内部,描述要测试的功能。在这里是 Login

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login' do

    end
  end
end

product_group 元数据

分配 product_group 元数据,并指定此测试属于哪个产品组。在这里是 authentication_and_authorization

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login', product_group: :authentication do

    end
  end
end

it 块(示例)

每个测试套件至少包含一个 it 块(示例)。编写端到端测试的一个好方法是编写测试用例描述作为 it 块:

module QA
  RSpec.describe 'Manage' do
    describe 'Login', product_group: :authentication do
      it 'can login' do

      end

      it 'can logout' do

      end
    end
  end
end

编写测试

一个重要的问题是"我们测试什么?",更重要的是"我们如何测试?"

首先从登录开始。

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login', product_group: :authentication do
      it 'can login' do
        Flow::Login.sign_in

      end

      it 'can logout' do
        Flow::Login.sign_in

      end
    end
  end
end

有关 Flows 的更多信息,请参阅 Flows

运行规范 后,我们的测试应该登录并结束;然后我们应该回答"我们测试什么?“这个问题。

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login', product_group: :authentication do
      it 'can login' do
        Flow::Login.sign_in

        Page::Main::Menu.perform do |menu|
          expect(menu).to be_signed_in
        end
      end

      it 'can logout' do
        Flow::Login.sign_in

        Page::Main::Menu.perform do |menu|
          menu.sign_out

          expect(menu).not_to be_signed_in
        end
      end
    end
  end
end

我们测试什么?

  1. 我们可以登录吗?
  2. 我们可以登出吗?

我们如何测试?

  1. 检查用户头像是否出现在左侧边栏。
  2. 检查用户头像 出现在左侧边栏。

在幕后,be_signed_in 是一个 predicate matcher,它 实现了检查用户头像

去除代码重复

重构您的测试,使用 before 块进行测试设置,因为它重复调用了 sign_in

# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login', product_group: :authentication do
      before do
        Flow::Login.sign_in
      end

      it 'can login' do
        Page::Main::Menu.perform do |menu|
          expect(menu).to be_signed_in
        end
      end

      it 'can logout' do
        Page::Main::Menu.perform do |menu|
          menu.sign_out

          expect(menu).not_to be_signed_in
        end
      end
    end
  end
end

before 块本质上是 before(:each),在每个示例运行之前执行,确保我们现在在每个测试开始时都登录。

使用资源和页面对象进行测试设置

接下来,让我们测试登录以外的功能。让我们测试问题(Issues),它属于计划阶段和项目管理组,因此在 创建文件qa/specs/features/browser_ui/2_plan/issue 目录下,命名为 issues_spec.rb

# frozen_string_literal: true

module QA
  RSpec.describe 'Plan' do
    describe 'Issues', product_group: :project_management do
      let(:issue) { create(:issue) }

      before do
        Flow::Login.sign_in
        issue.visit!
      end

      it 'can close an issue' do
        Page::Project::Issue::Show.perform do |show|
          show.click_close_issue_button

          expect(show).to be_closed
        end
      end
    end
  end
end

注意以下重要点:

  • 在我们的示例开始时,我们位于 page/issue/show.rb 页面
  • 我们的测试只按需创建所需内容。
  • 问题通过 API 创建以节省时间。
  • GitLab 偏好使用 let() 而不是实例变量。请参阅 最佳实践
  • be_closed 尚未在 page/project/issue/show.rb 中实现,但在下一步中实现。

问题被创建为 Resource,这是您可以通过 UI 或 API 创建的 GitLab 实体。其他示例包括:

编写页面对象

Page Object 是我们套件中的一个类,代表 GitLab 中的一个页面。登录页面就是一个例子。由于我们的 问题详情 页面的页面对象已经存在,添加 closed? 方法。

module Page::Project::Issue
  class Show
    view 'app/views/projects/issues/show.html.haml' do
      element 'closed-status-box'
    end

    def closed?
      has_element?('closed-status-box')
    end
  end
end

接下来,在您的视图中定义 closed-status-box 元素,以便您的页面对象可以识别它。

-#=> app/views/projects/issues/show.html.haml
.issuable-status-box.status-box.status-box-issue-closed{ ..., data: { testid: 'closed-status-box' } }

运行规范

在运行规范之前,请确保:

  • 已安装 GDK。
  • GDK 在本地端口 3000 上运行。
  • 没有应用额外的 RSpec 元数据标签
  • 您的工作目录是 GDK GitLab 安装中的 qa/
  • 您的 GitLab 实例级设置为默认值。如果您更改了默认设置,某些测试可能会产生意外结果。
  • 因为 GDK 首次登录需要更改密码,您必须包含 root 用户的 GDK 密码

要运行规范,请执行以下命令:

GITLAB_PASSWORD=<GDK root password> bundle exec rspec <test_file>

其中 <test_file> 是:

  • 运行登录示例时,使用 qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
  • 运行问题示例时,使用 qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb

有关测试执行和可能选项的更多信息,请参阅 “QA framework README”

准备代码审查

在提交测试进行代码审查之前,有几个清理工作要做:

  1. 确保测试名称遵循推荐的 命名约定
  2. 确保规范 链接到测试用例
  3. 确保规范具有正确的 product_group 元数据。有关组的完整列表,请参阅 Product sections, stages, groups, and categories
  4. 确保将相关的 RSpec 元数据 添加到规范中。
  5. 确保页面对象元素按照 推荐的命名约定 命名。

端到端测试合并请求模板

提交新的端到端测试时,使用 “New End to End Test” 合并请求描述模板,了解成功合并前所需的额外步骤。