637.md 11.6 KB
Newer Older
Lab机器人's avatar
readme  
Lab机器人 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
# Beginner's guide to writing end-to-end tests

> 原文:[https://docs.gitlab.com/ee/development/testing_guide/end_to_end/beginners_guide.html](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/beginners_guide.html)

*   [Before you write a test](#before-you-write-a-test)
*   [Determine if end-to-end tests are needed](#determine-if-end-to-end-tests-are-needed)
*   [Identify the DevOps stage](#identify-the-devops-stage)
*   [Create a skeleton test](#create-a-skeleton-test)
    *   [The outer `context` block](#the-outer-context-block)
    *   [The outer `RSpec.describe` block](#the-outer-rspecdescribe-block)
    *   [The `describe` block](#the-describe-block)
    *   [The `it` blocks (examples)](#the-it-blocks-examples)
*   [Write the test](#write-the-test)
*   [De-duplicate your code](#de-duplicate-your-code)
*   [Test setup using resources and page objects](#test-setup-using-resources-and-page-objects)
*   [Write the page object](#write-the-page-object)
*   [Run the spec](#run-the-spec)

# Beginner’s guide to writing end-to-end tests[](#beginners-guide-to-writing-end-to-end-tests "Permalink")

在本教程中,您将学习如何为[GitLab 社区版](https://about.gitlab.com/install/?version=ce)[GitLab 企业版](https://about.gitlab.com/install/)创建端到端( *e2e* )测试.

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

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

## Before you write a test[](#before-you-write-a-test "Permalink")

在编写测试之前,必须将您的[GitLab 开发套件(GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit)配置为运行规范. 端到端测试:

*   包含在`qa/`目录中.
*   应该是独立和[幂等的](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning) .
*   临时创建[资源](resources.html) (例如项目,问题,用户).
*   Test the UI and API interfaces, and use the API to efficiently set up the UI tests.

**提示:**有关更多信息,请参阅[端到端测试最佳实践](best_practices.html) .

## Determine if end-to-end tests are needed[](#determine-if-end-to-end-tests-are-needed "Permalink")

在编写端到端测试之前,请为[GitLab 社区版](https://gitlab-org.gitlab.io/gitlab-foss/coverage-ruby/#_AllFiles)[GitLab 企业版](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/#_AllFiles)项目检查特定功能的代码覆盖率. 在单元,功能或集成级别上是否存在足够的测试范围? 如果回答*是肯定的* ,那么你*就*需要一个终端到终端的测试.

有关 GitLab 中每个级别的测试分布的信息,请参见[测试级别](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md) .

*   请参阅[如何以正确的级别进行测试?](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md#how-to-test-at-the-correct-level) " [测试级别"](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md)文档的"部分".
*   查看功能更改的频率. 如果较低级别的测试中已经包含了稳定的特性,那么这些特性不会经常更改,那么端到端测试可能就不值得考虑.
*   最后,与参与实现功能和较低级别测试的开发人员讨论建议的测试.

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

## Identify the DevOps stage[](#identify-the-devops-stage "Permalink")

GitLab QA 端到端测试是由[DevOps 生命周期](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features/browser_ui)的不同[阶段](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features/browser_ui)组织的. 确定应按[阶段](https://about.gitlab.com/handbook/product/product-categories/#devops-stages)放置测试的位置,确定该测试属于哪个功能,然后将其放置在该阶段下的子目录中.

[![DevOps lifecycle by stages](img/31396caea61831939862213abb8dd10c.png)](img/gl-devops-lifecycle-by-stage-numbers_V12_10.png)

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

## Create a skeleton test[](#create-a-skeleton-test "Permalink")

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

### The outer `context` block[](#the-outer-context-block "Permalink")

参见[`RSpec.describe`外部块](#the-outer-rspecdescribe-block)

**弃用声明:**遵循 RSpec 4.0 规范在`13.2`[弃用](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/550)了外部`context` . 请改用`RSpec.describe` .

### The outer `RSpec.describe` block[](#the-outer-rspecdescribe-block "Permalink")

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

```
# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do

  end
end 
```

### The `describe` block[](#the-describe-block "Permalink")

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

```
# frozen_string_literal: true

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

    end
  end
end 
```

### The `it` blocks (examples)[](#the-it-blocks-examples "Permalink")

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

```
module QA
  RSpec.describe 'Manage' do
    describe 'Login' do
      it 'can login' do

      end

      it 'can logout' do

      end
    end
  end
end 
```

## Write the test[](#write-the-test "Permalink")

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

首先登录.

```
# frozen_string_literal: true

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

      end

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

      end
    end
  end
end 
```

[运行规范后](#run-the-spec) ,我们的测试应登录并结束; 那么我们应该回答"我们要测试什么?"这个问题.

```
# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login' 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`是一个[谓词匹配器](https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/built-in-matchers/predicate-matchers) ,可[实现对用户头像的检查](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/page/main/menu.rb#L74) .

## De-duplicate your code[](#de-duplicate-your-code "Permalink")

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

```
# frozen_string_literal: true

module QA
  RSpec.describe 'Manage' do
    describe 'Login' 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)`并且在每个示例之前运行,确保我们现在在每个测试的开始处登录.

## Test setup using resources and page objects[](#test-setup-using-resources-and-page-objects "Permalink")

接下来,让我们测试登录以外的其他功能. 让我们测试"计划"阶段拥有的"问题",因此在`qa/specs/features/browser_ui/3_create/issues` [创建一个](#identify-the-devops-stage)名为`issues_spec.rb` .

```
# frozen_string_literal: true

module QA
  RSpec.describe 'Plan' do
    describe 'Issues' do
      let(:issue) do
        Resource::Issue.fabricate_via_api! do |issue|
          issue.title = 'My issue'
          issue.description = 'This is an issue specific to this test'
        end
      end

      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` [页面](page_objects.html) .
*   我们的测试仅在需要时制造需要的东西.
*   该问题是通过 API 伪造的,以节省时间.
*   GitLab 更喜欢`let()`不是实例变量. 查看[最佳做法](../best_practices.html#let-variables) .
*   `be_closed`尚未在`page/project/issue/show.rb`中实现,但将在下一步中实现.

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

*   A [Merge Request](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/merge_request.rb).
*   A [User](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/user.rb).
*   A [Project](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/project.rb).
*   A [Group](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/group.rb).

## Write the page object[](#write-the-page-object "Permalink")

[Page Object](page_objects.html)是我们套件中的一个类,代表 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: { qa_selector: 'closed_status_box' } } 
```

## Run the spec[](#run-the-spec "Permalink")

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

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

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

```
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` .