# End-to-end testing Best Practices

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

*   [Prefer API over UI](#prefer-api-over-ui)
*   [Avoid superfluous expectations](#avoid-superfluous-expectations)
*   [Prefer to split tests across multiple files](#prefer-to-split-tests-across-multiple-files)
*   [Limit the use of the UI in `before(:context)` and `after` hooks](#limit-the-use-of-the-ui-in-beforecontext-and-after-hooks)
*   [Ensure tests do not leave the browser logged in](#ensure-tests-do-not-leave-the-browser-logged-in)
*   [Tag tests that require Administrator access](#tag-tests-that-require-administrator-access)
*   [Prefer `Commit` resource over `ProjectPush`](#prefer-commit-resource-over-projectpush)

# End-to-end testing Best Practices[](#end-to-end-testing-best-practices "Permalink")

**注意：**这是[测试指南中](../best_practices.html)最佳实践的量身定制的扩展.

## Prefer API over UI[](#prefer-api-over-ui "Permalink")

端到端测试框架能够根据具体情况来构造其资源. 应尽可能通过 API 来构造资源.

通过 API 构造测试所需的资源，我们可以节省时间和金钱.

[Learn more](resources.html) about resources.

## Avoid superfluous expectations[](#avoid-superfluous-expectations "Permalink")

为了使测试保持精简，重要的是我们仅测试需要测试的内容.

确保不添加任何与需要测试的内容无关的`expect()`语句.

例如：

```
#=> Good
Flow::Login.sign_in
Page::Main::Menu.perform do |menu|
  expect(menu).to be_signed_in
end

#=> Bad
Flow::Login.sign_in(as: user)
Page::Main::Menu.perform do |menu|
  expect(menu).to be_signed_in
  expect(page).to have_content(user.name) #=>  we already validated being signed in. redundant.
  expect(menu).to have_element(:nav_bar) #=> likely unnecessary. already validated in lower-level. test doesn't call for validating this.
end

#=> Good
issue = Resource::Issue.fabricate_via_api! do |issue|
  issue.name = 'issue-name'
end

Project::Issues::Index.perform do |index|
  expect(index).to have_issue(issue)
end

#=> Bad
issue = Resource::Issue.fabricate_via_api! do |issue|
  issue.name = 'issue-name'
end

Project::Issues::Index.perform do |index|
  expect(index).to have_issue(issue)
  expect(page).to have_content(issue.name) #=> page content check is redundant as the issue was already validated in the line above.
end 
```

## Prefer to split tests across multiple files[](#prefer-to-split-tests-across-multiple-files "Permalink")

我们的框架包括几个并行化机制，这些机制通过并行执行 spec 文件来工作.

但是，由于测试是通过 spec *文件*而不是通过 test / example 并行化的，因此如果将新测试添加到现有文件中，我们将无法实现更大的并行化.

尽管如此，可能还有其他原因要向现有文件添加新测试.

例如，如果测试共享状态的设置成本很高，则即使执行一次使用该设置的测试无法并行化，执行一次设置可能会更有效.

综上所述：

*   **可以** ：将测试拆分到单独的文件中，除非测试共享昂贵的设置.
*   **不要** ：在不考虑对并行化的影响的情况下，将新测试放在现有文件中.

## Limit the use of the UI in `before(:context)` and `after` hooks[](#limit-the-use-of-the-ui-in-beforecontext-and-after-hooks "Permalink")

限制使用`before(:context)`挂钩仅通过 API 调用，非 UI 操作或基本 UI 操作（例如登录`before(:context)`来执行设置任务.

我们使用[`capybara-screenshot`](https://github.com/mattheworiordan/capybara-screenshot)库自动保存失败时的屏幕截图.

`capybara-screenshot` [将屏幕](https://github.com/mattheworiordan/capybara-screenshot/blob/master/lib/capybara-screenshot/rspec.rb#L97) `capybara-screenshot` [保存在 RSpec 的`after` hook 中](https://github.com/mattheworiordan/capybara-screenshot/blob/master/lib/capybara-screenshot/rspec.rb#L97) . [如果`before(:context)`失败，则不会调用`after`挂钩](https://github.com/rspec/rspec-core/pull/2652/files#diff-5e04af96d5156e787f28d519a8c99615R148) ，因此不会保存屏幕截图.

鉴于这一事实，我们应该将`before(:context)`使用仅限于不需要屏幕快照的那些操作.

同样， `after`钩子仅应用于非 UI 操作. 测试文件中`after` hook 中的任何 UI 操作都将在获取屏幕快照的`after` hook 之前执行. 这将导致 UI 状态从故障点移开，因此无法在正确的时刻捕获屏幕截图.

## Ensure tests do not leave the browser logged in[](#ensure-tests-do-not-leave-the-browser-logged-in "Permalink")

所有测试都希望能够在测试开始时登录.

有关示例，请参见： [https](https://gitlab.com/gitlab-org/gitlab/-/issues/34736) : [//gitlab.com/gitlab-org/gitlab/-/issues/34736](https://gitlab.com/gitlab-org/gitlab/-/issues/34736)

理想情况下，在`after(:context)` （或[`before(:context)`](#limit-the-use-of-the-ui-in-beforecontext-and-after-hooks) ）块中执行的任何动作都可以通过 API 执行. 但是，如果有必要通过 UI 进行操作（例如，如果不存在 API 功能），请确保在该块的末尾注销.

```
after(:all) do
  login unless Page::Main::Menu.perform(&:signed_in?)

  # Do something while logged in

  Page::Main::Menu.perform(&:sign_out)
end 
```

## Tag tests that require Administrator access[](#tag-tests-that-require-administrator-access "Permalink")

我们不会运行需要管理员对生产环境进行访问的测试.

当您添加需要管理员访问权限的新测试时，请应用 RSpec 元数据`:requires_admin`以使该测试不会包含在针对生产环境和我们不想在其上运行这些测试的其他环境执行的测试套件中.

注意：在本地运行测试或配置管道时，可以将环境变量`QA_CAN_TEST_ADMIN_FEATURES`设置为`false`以跳过带有`:requires_admin`标记的测试.

## Prefer `Commit` resource over `ProjectPush`[](#prefer-commit-resource-over-projectpush "Permalink")

与[使用 API​​一致](#prefer-api-over-ui) ，请尽可能使用`Commit`资源.

`ProjectPush`通过 Git 命令行界面（CLI）使用原始 Shell 命令，而`Commit`资源则发出 HTTP 请求.

```
# Using a commit resource
Resource::Commit.fabricate_via_api! do |commit|
  commit.commit_message = 'Initial commit'
  commit.add_files([
    {file_path: 'README.md', content: 'Hello, GitLab'}
  ])
end

# Using a ProjectPush
Resource::Repository::ProjectPush.fabricate! do |push|
  push.commit_message = 'Initial commit'
  push.file_name = 'README.md'
  push.file_content = 'Hello, GitLab'
end 
```

**注意：**当您的测试要求测试 SSH 集成或使用 Git CLI 时，使用`ProjectPush`会出现一些例外.