info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
It's recommended that you reuse the issue created to plan the test. If one does not already exist you
can create the issue yourself. Alternatively, you can run the test in a pipeline that has reporting
enabled and the test-case issue reporter will automatically create a new issue.
Whether you create a new test-case issue or one is created automatically, you will need to manually add
a `testcase` RSpec metadata tag. In most cases, a single test will be associated with a single test-case
issue ([see below for exceptions](#exceptions)).
For example:
```ruby
RSpec.describe 'Stage' do
describe 'General description of the feature under test' do
it 'test name', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/:issue_id' do
...
end
it 'another test', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/:another_issue_id' do
...
end
end
end
```
### Exceptions
Most tests are defined by a single line of a `spec` file, which is why those tests can be linked to a
single test-case issue via the `testcase` tag.
However, some tests don't have a one-to-one relationship between a line of a `spec` file and a test-case
issue. This is because some tests are defined in a way that means a single line is associated with
multiple tests, including:
- Parallelized tests.
- Templated tests.
- Tests in shared examples that include more than one example.
In those and similar cases we can't assign a single `testcase` tag and so we rely on the test-case
reporter to programmatically determine the correct test-case issue based on the name and description of
the test. In such cases, the test-case reporter will automatically create a test-case issue the first time
the test runs, if no issue exists already.
In such a case, if you create the issue yourself or want to reuse an existing issue,
you must use this [end-to-end test issue template](https://gitlab.com/gitlab-org/quality/testcases/-/blob/master/.gitlab/issue_templates/End-to-end%20Test.md)
to format the issue description.
To illustrate, there are two tests in the shared examples in [`qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/47b17db82c38ab704a23b5ba5d296ea0c6a732c8/qa/qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb):
```ruby
shared_examples 'only user with access pushes and merges' do
it 'unselected maintainer user fails to push' do
...
end
it 'selected developer user pushes and merges' do
...
end
end
```
Consider the following test that includes the shared examples:
```ruby
RSpec.describe 'Create' do
describe 'Restricted protected branch push and merge' do
context 'when only one user is allowed to merge and push to a protected branch' do
...
it_behaves_like 'only user with access pushes and merges'
end
end
end
```
There would be two associated test-case issues, one for each shared example, with the following content:
Our framework includes a couple of parallelization mechanisms that work by executing spec files in parallel.
However, because tests are parallelized by spec *file* and not by test/example, we can't achieve greater parallelization if a new test is added to an existing file.
Nonetheless, there could be other reasons to add a new test to an existing file.
For example, if tests share state that is expensive to set up it might be more efficient to perform that setup once even if it means the tests that use the setup can't be parallelized.
In summary:
- **Do**: Split tests across separate files, unless the tests share expensive setup.
- **Don't**: Put new tests in an existing file without considering the impact on parallelization.
`capybara-screenshot` [saves the screenshot in the RSpec's `after` hook](https://github.com/mattheworiordan/capybara-screenshot/blob/master/lib/capybara-screenshot/rspec.rb#L97).
[If there is a failure in `before(:context)`, the `after` hook is not called](https://github.com/rspec/rspec-core/pull/2652/files#diff-5e04af96d5156e787f28d519a8c99615R148) and so the screenshot is not saved.
Given this fact, we should limit the use of `before(:context)` to only those operations where a screenshot is not needed.
We don't run tests that require Administrator access against our Production environments.
When you add a new test that requires Administrator access, apply the RSpec metadata `:requires_admin` so that the test will not be included in the test suites executed against Production and other environments on which we don't want to run those tests.
When running tests locally or configuring a pipeline, the environment variable `QA_CAN_TEST_ADMIN_FEATURES` can be set to `false` to skip tests that have the `:requires_admin` tag.
words, we want to make sure something is absent. For unit tests and feature specs,
we commonly use `not_to`
because RSpec's built-in matchers are negatable, as are Capybara's, which means the following two statements are
equivalent.
```ruby
except(page).not_to have_text('hidden')
except(page).to have_no_text('hidden')
```
Unfortunately, that's not automatically the case for the predicate methods that we add to our
[page objects](page_objects.md). We need to [create our own negatable matchers](https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/custom-matchers/define-a-custom-matcher#matcher-with-separate-logic-for-expect().to-and-expect().not-to).
The initial example uses the `have_job` matcher which is derived from the [`has_job?` predicate
method of the `Page::Project::Pipeline::Show` page object](https://gitlab.com/gitlab-org/gitlab/-/blob/87864b3047c23b4308f59c27a3757045944af447/qa/qa/page/project/pipeline/show.rb#L53).
To create a negatable matcher, we use `has_no_job?` for the negative case:
We are creating custom negatable matchers in `qa/spec/support/matchers`.
NOTE:
We need to create custom negatable matchers only for the predicate methods we've added to the test framework, and only if we're using `not_to`. If we use `to have_no_*` a negatable matcher is not necessary but it increases code readability.
The problem is that if `'a_job'` is present and we're waiting for it to disappear, this statement will fail.
Neither problem is present if we create a custom negatable matcher because the `has_no_job?` predicate method
would be used, which would wait only as long as necessary for the job to disappear.
Lastly, negatable matchers are preferred over using matchers of the form `have_no_*` because it's a common and familiar practice to negate matchers using `not_to`. If we facilitate that practice by adding negatable matchers, we make it easier for subsequent test authors to write efficient tests.