Beginner’s guide to writing end-to-end tests

原文:https://docs.gitlab.com/ee/development/testing_guide/end_to_end/beginners_guide.html

Beginner’s guide to writing end-to-end tests

在本教程中,您将学习如何为GitLab 社区版GitLab 企业版创建端到端( e2e )测试.

在本教程结束时,您将能够:

  • 确定是否需要端到端测试.
  • Understand the directory structure within qa/.
  • 编写一个基本的端到端测试,以验证登录功能.
  • 开发任何缺少的页面对象库.

Before you write a test

在编写测试之前,必须将您的GitLab 开发套件(GDK)配置为运行规范. 端到端测试:

  • 包含在qa/目录中.
  • 应该是独立和幂等的 .
  • 临时创建资源 (例如项目,问题,用户).
  • Test the UI and API interfaces, and use the API to efficiently set up the UI tests.

提示:有关更多信息,请参阅端到端测试最佳实践 .

Determine if end-to-end tests are needed

在编写端到端测试之前,请为GitLab 社区版GitLab 企业版项目检查特定功能的代码覆盖率. 在单元,功能或集成级别上是否存在足够的测试范围? 如果回答是肯定的 ,那么你需要一个终端到终端的测试.

有关 GitLab 中每个级别的测试分布的信息,请参见测试级别 .

  • 请参阅如何以正确的级别进行测试?测试级别”文档的”部分”.
  • 查看功能更改的频率. 如果较低级别的测试中已经包含了稳定的特性,那么这些特性不会经常更改,那么端到端测试可能就不值得考虑.
  • 最后,与参与实现功能和较低级别测试的开发人员讨论建议的测试.

注意:检查GitLab 社区版GitLab 企业版覆盖率项目,以检查是否为此功能编写了以前的测试. 为了分析代码覆盖率,您必须了解哪些应用程序文件实现了特定功能.注意:在本教程中,我们将编写一个登录端到端测试,即使它已经被较低级别的测试所覆盖,因为这是大多数端到端流的第一步,并且最容易理解.

Identify the DevOps stage

GitLab QA 端到端测试是由DevOps 生命周期的不同阶段组织的. 确定应按阶段放置测试的位置,确定该测试属于哪个功能,然后将其放置在该阶段下的子目录中.

DevOps lifecycle by stages

注意:如果该测试仅是企业版,则将在features/ee目录中创建该测试,但遵循相同的 DevOps 生命周期格式.

Create a skeleton test

在本教程的第一部分中,我们将测试由 Manage 阶段拥有的登录名. 在qa/specs/features/browser_ui/1_manage/login ,创建文件basic_login_spec.rb .

The outer context block

参见RSpec.describe外部块

弃用声明:遵循 RSpec 4.0 规范在13.2弃用了外部context . 请改用RSpec.describe .

The outer RSpec.describe block

规格具有外部RSpec.describe指示 DevOps 阶段.

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Manage' do
  4. end
  5. end

The describe block

在我们的外部RSpec.describe内部,描述要测试的功能. 在这种情况下,请Login .

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Manage' do
  4. describe 'Login' do
  5. end
  6. end
  7. end

The it blocks (examples)

每个测试套件包含至少一个it阻断(实施例). 一个好方法,开始写终端到终端的测试是编写测试用例描述为it块:

  1. module QA
  2. RSpec.describe 'Manage' do
  3. describe 'Login' do
  4. it 'can login' do
  5. end
  6. it 'can logout' do
  7. end
  8. end
  9. end
  10. end

Write the test

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

首先登录.

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Manage' do
  4. describe 'Login' do
  5. it 'can login' do
  6. Flow::Login.sign_in
  7. end
  8. it 'can logout' do
  9. Flow::Login.sign_in
  10. end
  11. end
  12. end
  13. end

运行规范后 ,我们的测试应登录并结束; 那么我们应该回答”我们要测试什么?”这个问题.

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Manage' do
  4. describe 'Login' do
  5. it 'can login' do
  6. Flow::Login.sign_in
  7. Page::Main::Menu.perform do |menu|
  8. expect(menu).to be_signed_in
  9. end
  10. end
  11. it 'can logout' do
  12. Flow::Login.sign_in
  13. Page::Main::Menu.perform do |menu|
  14. menu.sign_out
  15. expect(menu).not_to be_signed_in
  16. end
  17. end
  18. end
  19. end
  20. end

我们要测试什么?

  1. 我们可以登录吗?
  2. 我们可以注销吗?

我们如何测试?

  1. 检查用户头像是否出现在顶部导航中.
  2. 检查用户头像是否未出现在顶部导航中.

注意:在幕后, be_signed_in是一个谓词匹配器 ,可实现对用户头像的检查 .

De-duplicate your code

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

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Manage' do
  4. describe 'Login' do
  5. before do
  6. Flow::Login.sign_in
  7. end
  8. it 'can login' do
  9. Page::Main::Menu.perform do |menu|
  10. expect(menu).to be_signed_in
  11. end
  12. end
  13. it 'can logout' do
  14. Page::Main::Menu.perform do |menu|
  15. menu.sign_out
  16. expect(menu).not_to be_signed_in
  17. end
  18. end
  19. end
  20. end
  21. end

before块本质上是一个before(:each)并且在每个示例之前运行,确保我们现在在每个测试的开始处登录.

Test setup using resources and page objects

接下来,让我们测试登录以外的其他功能. 让我们测试”计划”阶段拥有的”问题”,因此在qa/specs/features/browser_ui/3_create/issues 创建一个名为issues_spec.rb .

  1. # frozen_string_literal: true
  2. module QA
  3. RSpec.describe 'Plan' do
  4. describe 'Issues' do
  5. let(:issue) do
  6. Resource::Issue.fabricate_via_api! do |issue|
  7. issue.title = 'My issue'
  8. issue.description = 'This is an issue specific to this test'
  9. end
  10. end
  11. before do
  12. Flow::Login.sign_in
  13. issue.visit!
  14. end
  15. it 'can close an issue' do
  16. Page::Project::Issue::Show.perform do |show|
  17. show.click_close_issue_button
  18. expect(show).to be_closed
  19. end
  20. end
  21. end
  22. end
  23. end

请注意以下要点:

  • 在我们的示例开始时,我们将在page/issue/show.rb 页面 .
  • 我们的测试仅在需要时制造需要的东西.
  • 该问题是通过 API 伪造的,以节省时间.
  • GitLab 更喜欢let()不是实例变量. 查看最佳做法 .
  • be_closed尚未在page/project/issue/show.rb中实现,但将在下一步中实现.

该问题被伪装成Resource ,它是您可以通过 UI 或 API 创建的 GitLab 实体. 其他示例包括:

Write the page object

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

  1. module Page::Project::Issue
  2. class Show
  3. view 'app/views/projects/issues/show.html.haml' do
  4. element :closed_status_box
  5. end
  6. def closed?
  7. has_element?(:closed_status_box)
  8. end
  9. end
  10. end

接下来,在视图中定义元素closed_status_box ,以便页面对象可以看到它.

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

Run the spec

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

  • GDK 已安装.
  • GDK 在本地 3000 端口上运行.
  • 尚未应用其他RSpec 元数据标签 .
  • 您的工作目录是 GDK GitLab 安装中的qa/ .

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

  1. bundle exec bin/qa Test::Instance::All http://localhost:3000 -- <test_file>

Where <test_file> is:

  • 运行”登录”示例时,请输入qa/specs/features/browser_ui/1_manage/login/login_spec.rb .
  • 运行”问题”示例时,请执行qa/specs/features/browser_ui/2_plan/issues/issue_spec.rb .